Extract ModCollectionIdentity.

This commit is contained in:
Ottermandias 2024-12-27 11:36:30 +01:00
parent fbbfe5e00d
commit 67305d507a
43 changed files with 270 additions and 252 deletions

@ -1 +1 @@
Subproject commit d9caded5efb7c9db0a273a43bb5f6d53cf4ace7f Subproject commit fcc96daa02633f673325c14aeea6b6b568924f1e

View file

@ -8,7 +8,7 @@ namespace Penumbra.Api.Api;
public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService
{ {
public Dictionary<Guid, string> GetCollections() public Dictionary<Guid, string> GetCollections()
=> collections.Storage.ToDictionary(c => c.Id, c => c.Name); => collections.Storage.ToDictionary(c => c.Identity.Id, c => c.Identity.Name);
public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier) public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier)
{ {
@ -17,14 +17,14 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
var list = new List<(Guid Id, string Name)>(4); var list = new List<(Guid Id, string Name)>(4);
if (Guid.TryParse(identifier, out var guid) && collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty) if (Guid.TryParse(identifier, out var guid) && collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty)
list.Add((collection.Id, collection.Name)); list.Add((collection.Identity.Id, collection.Identity.Name));
else if (identifier.Length >= 8) else if (identifier.Length >= 8)
list.AddRange(collections.Storage.Where(c => c.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase)) list.AddRange(collections.Storage.Where(c => c.Identity.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase))
.Select(c => (c.Id, c.Name))); .Select(c => (c.Identity.Id, c.Identity.Name)));
list.AddRange(collections.Storage list.AddRange(collections.Storage
.Where(c => string.Equals(c.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Id, c.Name))) .Where(c => string.Equals(c.Identity.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Identity.Id, c.Identity.Name)))
.Select(c => (c.Id, c.Name))); .Select(c => (c.Identity.Id, c.Identity.Name)));
return list; return list;
} }
@ -54,7 +54,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
return null; return null;
var collection = collections.Active.ByType((CollectionType)type); var collection = collections.Active.ByType((CollectionType)type);
return collection == null ? null : (collection.Id, collection.Name); return collection == null ? null : (collection.Identity.Id, collection.Identity.Name);
} }
internal (Guid Id, string Name)? GetCollection(byte type) internal (Guid Id, string Name)? GetCollection(byte type)
@ -64,17 +64,17 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
{ {
var id = helpers.AssociatedIdentifier(gameObjectIdx); var id = helpers.AssociatedIdentifier(gameObjectIdx);
if (!id.IsValid) if (!id.IsValid)
return (false, false, (collections.Active.Default.Id, collections.Active.Default.Name)); return (false, false, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name));
if (collections.Active.Individuals.TryGetValue(id, out var collection)) if (collections.Active.Individuals.TryGetValue(id, out var collection))
return (true, true, (collection.Id, collection.Name)); return (true, true, (collection.Identity.Id, collection.Identity.Name));
helpers.AssociatedCollection(gameObjectIdx, out collection); helpers.AssociatedCollection(gameObjectIdx, out collection);
return (true, false, (collection.Id, collection.Name)); return (true, false, (collection.Identity.Id, collection.Identity.Name));
} }
public Guid[] GetCollectionByName(string name) public Guid[] GetCollectionByName(string name)
=> collections.Storage.Where(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Id).ToArray(); => collections.Storage.Where(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Identity.Id).ToArray();
public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId, public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId,
bool allowCreateNew, bool allowDelete) bool allowCreateNew, bool allowDelete)
@ -83,7 +83,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
return (PenumbraApiEc.InvalidArgument, null); return (PenumbraApiEc.InvalidArgument, null);
var oldCollection = collections.Active.ByType((CollectionType)type); var oldCollection = collections.Active.ByType((CollectionType)type);
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?(); var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple<Guid, string>?();
if (collectionId == null) if (collectionId == null)
{ {
if (old == null) if (old == null)
@ -106,7 +106,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
collections.Active.CreateSpecialCollection((CollectionType)type); collections.Active.CreateSpecialCollection((CollectionType)type);
} }
else if (old.Value.Item1 == collection.Id) else if (old.Value.Item1 == collection.Identity.Id)
{ {
return (PenumbraApiEc.NothingChanged, old); return (PenumbraApiEc.NothingChanged, old);
} }
@ -120,10 +120,10 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
{ {
var id = helpers.AssociatedIdentifier(gameObjectIdx); var id = helpers.AssociatedIdentifier(gameObjectIdx);
if (!id.IsValid) if (!id.IsValid)
return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Id, collections.Active.Default.Name)); return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name));
var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null; var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null;
var old = oldCollection != null ? (oldCollection.Id, oldCollection.Name) : new ValueTuple<Guid, string>?(); var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple<Guid, string>?();
if (collectionId == null) if (collectionId == null)
{ {
if (old == null) if (old == null)
@ -148,7 +148,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
var ids = collections.Active.Individuals.GetGroup(id); var ids = collections.Active.Individuals.GetGroup(id);
collections.Active.CreateIndividualCollection(ids); collections.Active.CreateIndividualCollection(ids);
} }
else if (old.Value.Item1 == collection.Id) else if (old.Value.Item1 == collection.Identity.Id)
{ {
return (PenumbraApiEc.NothingChanged, old); return (PenumbraApiEc.NothingChanged, old);
} }

View file

@ -61,7 +61,7 @@ public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject) public unsafe (nint GameObject, (Guid Id, string Name) Collection) GetDrawObjectInfo(nint drawObject)
{ {
var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true); var data = _collectionResolver.IdentifyCollection((DrawObject*)drawObject, true);
return (data.AssociatedGameObject, (data.ModCollection.Id, data.ModCollection.Name)); return (data.AssociatedGameObject, (Id: data.ModCollection.Identity.Id, Name: data.ModCollection.Identity.Name));
} }
public int GetCutsceneParentIndex(int actorIdx) public int GetCutsceneParentIndex(int actorIdx)
@ -93,5 +93,5 @@ public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
} }
private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject) private void OnCreatedCharacterBase(nint gameObject, ModCollection collection, nint drawObject)
=> CreatedCharacterBase?.Invoke(gameObject, collection.Id, drawObject); => CreatedCharacterBase?.Invoke(gameObject, collection.Identity.Id, drawObject);
} }

View file

@ -77,7 +77,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
if (!_collectionManager.Storage.ById(collectionId, out var collection)) if (!_collectionManager.Storage.ById(collectionId, out var collection))
return (PenumbraApiEc.CollectionMissing, null); return (PenumbraApiEc.CollectionMissing, null);
var settings = collection.Id == Guid.Empty var settings = collection.Identity.Id == Guid.Empty
? null ? null
: ignoreInheritance : ignoreInheritance
? collection.Settings[mod.Index] ? collection.Settings[mod.Index]
@ -217,7 +217,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
var collection = _collectionResolver.PlayerCollection(); var collection = _collectionResolver.PlayerCollection();
var (settings, parent) = collection[mod.Index]; var (settings, parent) = collection[mod.Index];
if (settings is { Enabled: true }) if (settings is { Enabled: true })
ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Id, mod.Identifier, parent != collection); ModSettingChanged?.Invoke(ModSettingChange.Edited, collection.Identity.Id, mod.Identifier, parent != collection);
} }
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2) private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? _1, DirectoryInfo? _2)
@ -227,7 +227,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
} }
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited) private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting _1, int _2, bool inherited)
=> ModSettingChanged?.Invoke(type, collection.Id, mod?.ModPath.Name ?? string.Empty, inherited); => ModSettingChanged?.Invoke(type, collection.Identity.Id, mod?.ModPath.Name ?? string.Empty, inherited);
private void OnModOptionEdited(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container, private void OnModOptionEdited(ModOptionChangeType type, Mod mod, IModGroup? group, IModOption? option, IModDataContainer? container,
int moveIndex) int moveIndex)

View file

@ -146,10 +146,10 @@ public class TemporaryIpcTester(
using (ImRaii.PushFont(UiBuilder.MonoFont)) using (ImRaii.PushFont(UiBuilder.MonoFont))
{ {
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable(collection.Identifier); ImGuiUtil.CopyOnClickSelectable(collection.Identity.Identifier);
} }
ImGuiUtil.DrawTableColumn(collection.Name); ImGuiUtil.DrawTableColumn(collection.Identity.Name);
ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString()); ImGuiUtil.DrawTableColumn(collection.ResolvedFiles.Count.ToString());
ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0"); ImGuiUtil.DrawTableColumn(collection.MetaCache?.Count.ToString() ?? "0");
ImGuiUtil.DrawTableColumn(string.Join(", ", ImGuiUtil.DrawTableColumn(string.Join(", ",
@ -199,7 +199,7 @@ public class TemporaryIpcTester(
{ {
PrintList("All", tempMods.ModsForAllCollections); PrintList("All", tempMods.ModsForAllCollections);
foreach (var (collection, list) in tempMods.Mods) foreach (var (collection, list) in tempMods.Mods)
PrintList(collection.Name, list); PrintList(collection.Identity.Name, list);
} }
} }
} }

View file

@ -85,13 +85,13 @@ public class TempModManager : IDisposable, IService
{ {
if (removed) if (removed)
{ {
Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.AnonymizedName}."); Penumbra.Log.Verbose($"Removing temporary Mod {mod.Name} from {collection.Identity.AnonymizedName}.");
collection.Remove(mod); collection.Remove(mod);
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false); _communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
} }
else else
{ {
Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.AnonymizedName}."); Penumbra.Log.Verbose($"Adding {(created ? "new " : string.Empty)}temporary Mod {mod.Name} to {collection.Identity.AnonymizedName}.");
collection.Apply(mod, created); collection.Apply(mod, created);
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false); _communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.True, 0, false);
} }

View file

@ -31,7 +31,7 @@ public sealed class CollectionCache : IDisposable
public int Calculating = -1; public int Calculating = -1;
public string AnonymizedName public string AnonymizedName
=> _collection.AnonymizedName; => _collection.Identity.AnonymizedName;
public IEnumerable<SingleArray<ModConflicts>> AllConflicts public IEnumerable<SingleArray<ModConflicts>> AllConflicts
=> ConflictDict.Values; => ConflictDict.Values;

View file

@ -114,16 +114,16 @@ public class CollectionCacheManager : IDisposable, IService
/// <summary> Only creates a new cache, does not update an existing one. </summary> /// <summary> Only creates a new cache, does not update an existing one. </summary>
public bool CreateCache(ModCollection collection) public bool CreateCache(ModCollection collection)
{ {
if (collection.Index == ModCollection.Empty.Index) if (collection.Identity.Index == ModCollection.Empty.Identity.Index)
return false; return false;
if (collection._cache != null) if (collection._cache != null)
return false; return false;
collection._cache = new CollectionCache(this, collection); collection._cache = new CollectionCache(this, collection);
if (collection.Index > 0) if (collection.Identity.Index > 0)
Interlocked.Increment(ref _count); Interlocked.Increment(ref _count);
Penumbra.Log.Verbose($"Created new cache for collection {collection.AnonymizedName}."); Penumbra.Log.Verbose($"Created new cache for collection {collection.Identity.AnonymizedName}.");
return true; return true;
} }
@ -132,32 +132,32 @@ public class CollectionCacheManager : IDisposable, IService
/// Does not create caches. /// Does not create caches.
/// </summary> /// </summary>
public void CalculateEffectiveFileList(ModCollection collection) public void CalculateEffectiveFileList(ModCollection collection)
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identifier, => _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identity.Identifier,
() => CalculateEffectiveFileListInternal(collection)); () => CalculateEffectiveFileListInternal(collection));
private void CalculateEffectiveFileListInternal(ModCollection collection) private void CalculateEffectiveFileListInternal(ModCollection collection)
{ {
// Skip the empty collection. // Skip the empty collection.
if (collection.Index == 0) if (collection.Identity.Index == 0)
return; return;
Penumbra.Log.Debug($"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName}"); Penumbra.Log.Debug($"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName}");
if (!collection.HasCache) if (!collection.HasCache)
{ {
Penumbra.Log.Error( Penumbra.Log.Error(
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName} failed, no cache exists."); $"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName} failed, no cache exists.");
} }
else if (collection._cache!.Calculating != -1) else if (collection._cache!.Calculating != -1)
{ {
Penumbra.Log.Error( Penumbra.Log.Error(
$"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.AnonymizedName} failed, already in calculation on [{collection._cache!.Calculating}]."); $"[{Environment.CurrentManagedThreadId}] Recalculating effective file list for {collection.Identity.AnonymizedName} failed, already in calculation on [{collection._cache!.Calculating}].");
} }
else else
{ {
FullRecalculation(collection); FullRecalculation(collection);
Penumbra.Log.Debug( Penumbra.Log.Debug(
$"[{Environment.CurrentManagedThreadId}] Recalculation of effective file list for {collection.AnonymizedName} finished."); $"[{Environment.CurrentManagedThreadId}] Recalculation of effective file list for {collection.Identity.AnonymizedName} finished.");
} }
} }
@ -213,7 +213,7 @@ public class CollectionCacheManager : IDisposable, IService
else else
{ {
RemoveCache(old); RemoveCache(old);
if (type is not CollectionType.Inactive && newCollection != null && newCollection.Index != 0 && CreateCache(newCollection)) if (type is not CollectionType.Inactive && newCollection != null && newCollection.Identity.Index != 0 && CreateCache(newCollection))
CalculateEffectiveFileList(newCollection); CalculateEffectiveFileList(newCollection);
if (type is CollectionType.Default) if (type is CollectionType.Default)
@ -258,12 +258,12 @@ public class CollectionCacheManager : IDisposable, IService
private void RemoveCache(ModCollection? collection) private void RemoveCache(ModCollection? collection)
{ {
if (collection != null if (collection != null
&& collection.Index > ModCollection.Empty.Index && collection.Identity.Index > ModCollection.Empty.Identity.Index
&& collection.Index != _active.Default.Index && collection.Identity.Index != _active.Default.Identity.Index
&& collection.Index != _active.Interface.Index && collection.Identity.Index != _active.Interface.Identity.Index
&& collection.Index != _active.Current.Index && collection.Identity.Index != _active.Current.Identity.Index
&& _active.SpecialAssignments.All(c => c.Value.Index != collection.Index) && _active.SpecialAssignments.All(c => c.Value.Identity.Index != collection.Identity.Index)
&& _active.Individuals.All(c => c.Collection.Index != collection.Index)) && _active.Individuals.All(c => c.Collection.Identity.Index != collection.Identity.Index))
ClearCache(collection); ClearCache(collection);
} }
@ -359,9 +359,9 @@ public class CollectionCacheManager : IDisposable, IService
collection._cache!.Dispose(); collection._cache!.Dispose();
collection._cache = null; collection._cache = null;
if (collection.Index > 0) if (collection.Identity.Index > 0)
Interlocked.Decrement(ref _count); Interlocked.Decrement(ref _count);
Penumbra.Log.Verbose($"Cleared cache of collection {collection.AnonymizedName}."); Penumbra.Log.Verbose($"Cleared cache of collection {collection.Identity.AnonymizedName}.");
} }
/// <summary> /// <summary>

View file

@ -59,7 +59,7 @@ public sealed class CollectionAutoSelector : IService, IDisposable
return; return;
var collection = _resolver.PlayerCollection(); var collection = _resolver.PlayerCollection();
Penumbra.Log.Debug($"Setting current collection to {collection.Identifier} through automatic collection selection."); Penumbra.Log.Debug($"Setting current collection to {collection.Identity.Identifier} through automatic collection selection.");
_collections.SetCollection(collection, CollectionType.Current); _collections.SetCollection(collection, CollectionType.Current);
} }

View file

@ -48,7 +48,7 @@ public static class ActiveCollectionMigration
if (!storage.ByName(collectionName, out var collection)) if (!storage.ByName(collectionName, out var collection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning); $"Last choice of <{player}>'s Collection {collectionName} is not available, reset to {ModCollection.Empty.Identity.Name}.", NotificationType.Warning);
dict.Add(player, ModCollection.Empty); dict.Add(player, ModCollection.Empty);
} }
else else

View file

@ -219,7 +219,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
_ => null, _ => null,
}; };
if (oldCollection == null || collection == oldCollection || collection.Index >= _storage.Count) if (oldCollection == null || collection == oldCollection || collection.Identity.Index >= _storage.Count)
return; return;
switch (collectionType) switch (collectionType)
@ -262,13 +262,13 @@ public class ActiveCollections : ISavable, IDisposable, IService
var jObj = new JObject var jObj = new JObject
{ {
{ nameof(Version), Version }, { nameof(Version), Version },
{ nameof(Default), Default.Id }, { nameof(Default), Default.Identity.Id },
{ nameof(Interface), Interface.Id }, { nameof(Interface), Interface.Identity.Id },
{ nameof(Current), Current.Id }, { nameof(Current), Current.Identity.Id },
}; };
foreach (var (type, collection) in SpecialCollections.WithIndex().Where(p => p.Value != null) foreach (var (type, collection) in SpecialCollections.WithIndex().Where(p => p.Value != null)
.Select(p => ((CollectionType)p.Index, p.Value!))) .Select(p => ((CollectionType)p.Index, p.Value!)))
jObj.Add(type.ToString(), collection.Id); jObj.Add(type.ToString(), collection.Identity.Id);
jObj.Add(nameof(Individuals), Individuals.ToJObject()); jObj.Add(nameof(Individuals), Individuals.ToJObject());
using var j = new JsonTextWriter(writer); using var j = new JsonTextWriter(writer);
@ -300,7 +300,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (oldCollection == Interface) if (oldCollection == Interface)
SetCollection(ModCollection.Empty, CollectionType.Interface); SetCollection(ModCollection.Empty, CollectionType.Interface);
if (oldCollection == Current) if (oldCollection == Current)
SetCollection(Default.Index > ModCollection.Empty.Index ? Default : _storage.DefaultNamed, CollectionType.Current); SetCollection(Default.Identity.Index > ModCollection.Empty.Identity.Index ? Default : _storage.DefaultNamed, CollectionType.Current);
for (var i = 0; i < SpecialCollections.Length; ++i) for (var i = 0; i < SpecialCollections.Length; ++i)
{ {
@ -325,11 +325,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
{ {
var configChanged = false; var configChanged = false;
// Load the default collection. If the name does not exist take the empty collection. // Load the default collection. If the name does not exist take the empty collection.
var defaultName = jObject[nameof(Default)]?.ToObject<string>() ?? ModCollection.Empty.Name; var defaultName = jObject[nameof(Default)]?.ToObject<string>() ?? ModCollection.Empty.Identity.Name;
if (!_storage.ByName(defaultName, out var defaultCollection)) if (!_storage.ByName(defaultName, out var defaultCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Identity.Name}.",
NotificationType.Warning); NotificationType.Warning);
Default = ModCollection.Empty; Default = ModCollection.Empty;
configChanged = true; configChanged = true;
@ -340,11 +340,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
} }
// Load the interface collection. If no string is set, use the name of whatever was set as Default. // Load the interface collection. If no string is set, use the name of whatever was set as Default.
var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Name; var interfaceName = jObject[nameof(Interface)]?.ToObject<string>() ?? Default.Identity.Name;
if (!_storage.ByName(interfaceName, out var interfaceCollection)) if (!_storage.ByName(interfaceName, out var interfaceCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Identity.Name}.",
NotificationType.Warning); NotificationType.Warning);
Interface = ModCollection.Empty; Interface = ModCollection.Empty;
configChanged = true; configChanged = true;
@ -355,11 +355,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
} }
// Load the current collection. // Load the current collection.
var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Name; var currentName = jObject[nameof(Current)]?.ToObject<string>() ?? Default.Identity.Name;
if (!_storage.ByName(currentName, out var currentCollection)) if (!_storage.ByName(currentName, out var currentCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.", $"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollectionIdentity.DefaultCollectionName}.",
NotificationType.Warning); NotificationType.Warning);
Current = _storage.DefaultNamed; Current = _storage.DefaultNamed;
configChanged = true; configChanged = true;
@ -404,7 +404,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (!_storage.ById(defaultId, out var defaultCollection)) if (!_storage.ById(defaultId, out var defaultCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.DefaultCollection} {defaultId} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.DefaultCollection} {defaultId} is not available, reset to {ModCollection.Empty.Identity.Name}.",
NotificationType.Warning); NotificationType.Warning);
Default = ModCollection.Empty; Default = ModCollection.Empty;
configChanged = true; configChanged = true;
@ -415,11 +415,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
} }
// Load the interface collection. If no string is set, use the name of whatever was set as Default. // Load the interface collection. If no string is set, use the name of whatever was set as Default.
var interfaceId = jObject[nameof(Interface)]?.ToObject<Guid>() ?? Default.Id; var interfaceId = jObject[nameof(Interface)]?.ToObject<Guid>() ?? Default.Identity.Id;
if (!_storage.ById(interfaceId, out var interfaceCollection)) if (!_storage.ById(interfaceId, out var interfaceCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.InterfaceCollection} {interfaceId} is not available, reset to {ModCollection.Empty.Name}.", $"Last choice of {TutorialService.InterfaceCollection} {interfaceId} is not available, reset to {ModCollection.Empty.Identity.Name}.",
NotificationType.Warning); NotificationType.Warning);
Interface = ModCollection.Empty; Interface = ModCollection.Empty;
configChanged = true; configChanged = true;
@ -430,11 +430,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
} }
// Load the current collection. // Load the current collection.
var currentId = jObject[nameof(Current)]?.ToObject<Guid>() ?? _storage.DefaultNamed.Id; var currentId = jObject[nameof(Current)]?.ToObject<Guid>() ?? _storage.DefaultNamed.Identity.Id;
if (!_storage.ById(currentId, out var currentCollection)) if (!_storage.ById(currentId, out var currentCollection))
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Last choice of {TutorialService.SelectedCollection} {currentId} is not available, reset to {ModCollection.DefaultCollectionName}.", $"Last choice of {TutorialService.SelectedCollection} {currentId} is not available, reset to {ModCollectionIdentity.DefaultCollectionName}.",
NotificationType.Warning); NotificationType.Warning);
Current = _storage.DefaultNamed; Current = _storage.DefaultNamed;
configChanged = true; configChanged = true;
@ -587,7 +587,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
case IdentifierType.Player when id.HomeWorld != ushort.MaxValue: case IdentifierType.Player when id.HomeWorld != ushort.MaxValue:
{ {
var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue)); var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue));
return global?.Index == checkAssignment.Index return (global != null ? global.Identity.Index : null) == checkAssignment.Identity.Index
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it." ? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
: string.Empty; : string.Empty;
} }
@ -596,12 +596,12 @@ public class ActiveCollections : ISavable, IDisposable, IService
{ {
var global = ByType(CollectionType.Individual, var global = ByType(CollectionType.Individual,
_actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId)); _actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
if (global?.Index == checkAssignment.Index) if ((global != null ? global.Identity.Index : null) == checkAssignment.Identity.Index)
return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."; return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it.";
} }
var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId)); var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId));
return unowned?.Index == checkAssignment.Index return (unowned != null ? unowned.Identity.Index : null) == checkAssignment.Identity.Index
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it." ? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
: string.Empty; : string.Empty;
} }
@ -617,7 +617,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (maleNpc == null) if (maleNpc == null)
{ {
maleNpc = Default; maleNpc = Default;
if (maleNpc.Index != checkAssignment.Index) if (maleNpc.Identity.Index != checkAssignment.Identity.Index)
return string.Empty; return string.Empty;
collection1 = CollectionType.Default; collection1 = CollectionType.Default;
@ -626,7 +626,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (femaleNpc == null) if (femaleNpc == null)
{ {
femaleNpc = Default; femaleNpc = Default;
if (femaleNpc.Index != checkAssignment.Index) if (femaleNpc.Identity.Index != checkAssignment.Identity.Index)
return string.Empty; return string.Empty;
collection2 = CollectionType.Default; collection2 = CollectionType.Default;
@ -646,7 +646,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (assignment == null) if (assignment == null)
continue; continue;
if (assignment.Index == checkAssignment.Index) if (assignment.Identity.Index == checkAssignment.Identity.Index)
return return
$"Assignment is currently redundant due to overwriting {parentType.ToName()} with an identical collection.\nYou can remove it."; $"Assignment is currently redundant due to overwriting {parentType.ToName()} with an identical collection.\nYou can remove it.";
} }

View file

@ -41,7 +41,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
public ModCollection CreateFromData(Guid id, string name, int version, Dictionary<string, ModSettings.SavedSettings> allSettings, public ModCollection CreateFromData(Guid id, string name, int version, Dictionary<string, ModSettings.SavedSettings> allSettings,
IReadOnlyList<string> inheritances) IReadOnlyList<string> inheritances)
{ {
var newCollection = ModCollection.CreateFromData(_saveService, _modStorage, id, name, CurrentCollectionId, version, Count, allSettings, var newCollection = ModCollection.CreateFromData(_saveService, _modStorage, new ModCollectionIdentity(id, CurrentCollectionId, name, Count), version, allSettings,
inheritances); inheritances);
_collectionsByLocal[CurrentCollectionId] = newCollection; _collectionsByLocal[CurrentCollectionId] = newCollection;
CurrentCollectionId += 1; CurrentCollectionId += 1;
@ -57,7 +57,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
} }
public void Delete(ModCollection collection) public void Delete(ModCollection collection)
=> _collectionsByLocal.Remove(collection.LocalId); => _collectionsByLocal.Remove(collection.Identity.LocalId);
/// <remarks> The empty collection is always available at Index 0. </remarks> /// <remarks> The empty collection is always available at Index 0. </remarks>
private readonly List<ModCollection> _collections = private readonly List<ModCollection> _collections =
@ -92,7 +92,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
public bool ByName(string name, [NotNullWhen(true)] out ModCollection? collection) public bool ByName(string name, [NotNullWhen(true)] out ModCollection? collection)
{ {
if (name.Length != 0) if (name.Length != 0)
return _collections.FindFirst(c => string.Equals(c.Name, name, StringComparison.OrdinalIgnoreCase), out collection); return _collections.FindFirst(c => string.Equals(c.Identity.Name, name, StringComparison.OrdinalIgnoreCase), out collection);
collection = ModCollection.Empty; collection = ModCollection.Empty;
return true; return true;
@ -102,7 +102,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection) public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
{ {
if (id != Guid.Empty) if (id != Guid.Empty)
return _collections.FindFirst(c => c.Id == id, out collection); return _collections.FindFirst(c => c.Identity.Id == id, out collection);
collection = ModCollection.Empty; collection = ModCollection.Empty;
return true; return true;
@ -158,7 +158,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
var newCollection = Create(name, _collections.Count, duplicate); var newCollection = Create(name, _collections.Count, duplicate);
_collections.Add(newCollection); _collections.Add(newCollection);
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection)); _saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.AnonymizedName}.", NotificationType.Success, false); Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.Identity.AnonymizedName}.", NotificationType.Success, false);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty); _communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
return true; return true;
} }
@ -168,13 +168,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
/// </summary> /// </summary>
public bool RemoveCollection(ModCollection collection) public bool RemoveCollection(ModCollection collection)
{ {
if (collection.Index <= ModCollection.Empty.Index || collection.Index >= _collections.Count) if (collection.Identity.Index <= ModCollection.Empty.Identity.Index || collection.Identity.Index >= _collections.Count)
{ {
Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false); Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false);
return false; return false;
} }
if (collection.Index == DefaultNamed.Index) if (collection.Identity.Index == DefaultNamed.Identity.Index)
{ {
Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false); Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
return false; return false;
@ -182,13 +182,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
Delete(collection); Delete(collection);
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection)); _saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
_collections.RemoveAt(collection.Index); _collections.RemoveAt(collection.Identity.Index);
// Update indices. // Update indices.
for (var i = collection.Index; i < Count; ++i) for (var i = collection.Identity.Index; i < Count; ++i)
_collections[i].Index = i; _collections[i].Identity.Index = i;
_collectionsByLocal.Remove(collection.LocalId); _collectionsByLocal.Remove(collection.Identity.LocalId);
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.AnonymizedName}.", NotificationType.Success, false); Penumbra.Messager.NotificationMessage($"Deleted collection {collection.Identity.AnonymizedName}.", NotificationType.Success, false);
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty); _communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
return true; return true;
} }
@ -246,13 +246,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
{ {
File.Move(file.FullName, correctName, false); File.Move(file.FullName, correctName, false);
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Collection {file.Name} does not correspond to {collection.Identifier}, renamed.", $"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, renamed.",
NotificationType.Warning); NotificationType.Warning);
} }
catch (Exception ex) catch (Exception ex)
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Collection {file.Name} does not correspond to {collection.Identifier}, rename failed:\n{ex}", $"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, rename failed:\n{ex}",
NotificationType.Warning); NotificationType.Warning);
} }
} }
@ -273,7 +273,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
catch (Exception e) catch (Exception e)
{ {
Penumbra.Messager.NotificationMessage(e, Penumbra.Messager.NotificationMessage(e,
$"Collection {file.Name} does not correspond to {collection.Identifier}, but could not rename.", $"Collection {file.Name} does not correspond to {collection.Identity.Identifier}, but could not rename.",
NotificationType.Error); NotificationType.Error);
} }
@ -291,14 +291,14 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
/// </summary> /// </summary>
private ModCollection SetDefaultNamedCollection() private ModCollection SetDefaultNamedCollection()
{ {
if (ByName(ModCollection.DefaultCollectionName, out var collection)) if (ByName(ModCollectionIdentity.DefaultCollectionName, out var collection))
return collection; return collection;
if (AddCollection(ModCollection.DefaultCollectionName, null)) if (AddCollection(ModCollectionIdentity.DefaultCollectionName, null))
return _collections[^1]; return _collections[^1];
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Unknown problem creating a collection with the name {ModCollection.DefaultCollectionName}, which is required to exist.", $"Unknown problem creating a collection with the name {ModCollectionIdentity.DefaultCollectionName}, which is required to exist.",
NotificationType.Error); NotificationType.Error);
return Count > 1 ? _collections[1] : _collections[0]; return Count > 1 ? _collections[1] : _collections[0];
} }

View file

@ -18,7 +18,7 @@ public partial class IndividualCollections
foreach (var (name, identifiers, collection) in Assignments) foreach (var (name, identifiers, collection) in Assignments)
{ {
var tmp = identifiers[0].ToJson(); var tmp = identifiers[0].ToJson();
tmp.Add("Collection", collection.Id); tmp.Add("Collection", collection.Identity.Id);
tmp.Add("Display", name); tmp.Add("Display", name);
ret.Add(tmp); ret.Add(tmp);
} }
@ -182,7 +182,7 @@ public partial class IndividualCollections
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}]."); Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
else else
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {name} ({collection.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.", $"Could not migrate {name} ({collection.Identity.AnonymizedName}) which was assumed to be a {kind.ToName()} with IDs [{ids}], please look through your individual collections.",
NotificationType.Error); NotificationType.Error);
} }
// If it is not a valid NPC name, check if it can be a player name. // If it is not a valid NPC name, check if it can be a player name.
@ -192,16 +192,16 @@ public partial class IndividualCollections
var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}.")); var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}."));
// Try to migrate the player name without logging full names. // Try to migrate the player name without logging full names.
if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection)) if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier."); Penumbra.Log.Information($"Migrated {shortName} ({collection.Identity.AnonymizedName}) to Player Identifier.");
else else
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {shortName} ({collection.AnonymizedName}), please look through your individual collections.", $"Could not migrate {shortName} ({collection.Identity.AnonymizedName}), please look through your individual collections.",
NotificationType.Error); NotificationType.Error);
} }
else else
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Could not migrate {name} ({collection.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.", $"Could not migrate {name} ({collection.Identity.AnonymizedName}), which can not be a player name nor is it a known NPC name, please look through your individual collections.",
NotificationType.Error); NotificationType.Error);
} }
} }

View file

@ -89,7 +89,7 @@ public class InheritanceManager : IDisposable, IService
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor); RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Removed {parent.AnonymizedName} from {inheritor.AnonymizedName} inheritances."); Penumbra.Log.Debug($"Removed {parent.Identity.AnonymizedName} from {inheritor.Identity.AnonymizedName} inheritances.");
} }
/// <summary> Order in the inheritance list is relevant. </summary> /// <summary> Order in the inheritance list is relevant. </summary>
@ -101,7 +101,7 @@ public class InheritanceManager : IDisposable, IService
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor)); _saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false); _communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
RecurseInheritanceChanges(inheritor); RecurseInheritanceChanges(inheritor);
Penumbra.Log.Debug($"Moved {inheritor.AnonymizedName}s inheritance {from} to {to}."); Penumbra.Log.Debug($"Moved {inheritor.Identity.AnonymizedName}s inheritance {from} to {to}.");
} }
/// <inheritdoc cref="AddInheritance(ModCollection, ModCollection)"/> /// <inheritdoc cref="AddInheritance(ModCollection, ModCollection)"/>
@ -119,7 +119,7 @@ public class InheritanceManager : IDisposable, IService
RecurseInheritanceChanges(inheritor); RecurseInheritanceChanges(inheritor);
} }
Penumbra.Log.Debug($"Added {parent.AnonymizedName} to {inheritor.AnonymizedName} inheritances."); Penumbra.Log.Debug($"Added {parent.Identity.AnonymizedName} to {inheritor.Identity.AnonymizedName} inheritances.");
return true; return true;
} }
@ -143,23 +143,23 @@ public class InheritanceManager : IDisposable, IService
continue; continue;
changes = true; changes = true;
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", Penumbra.Messager.NotificationMessage($"{collection.Identity.Name} can not inherit from {subCollection.Identity.Name}, removed.",
NotificationType.Warning); NotificationType.Warning);
} }
else if (_storage.ByName(subCollectionName, out subCollection)) else if (_storage.ByName(subCollectionName, out subCollection))
{ {
changes = true; changes = true;
Penumbra.Log.Information($"Migrating inheritance for {collection.AnonymizedName} from name to GUID."); Penumbra.Log.Information($"Migrating inheritance for {collection.Identity.AnonymizedName} from name to GUID.");
if (AddInheritance(collection, subCollection, false)) if (AddInheritance(collection, subCollection, false))
continue; continue;
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", Penumbra.Messager.NotificationMessage($"{collection.Identity.Name} can not inherit from {subCollection.Identity.Name}, removed.",
NotificationType.Warning); NotificationType.Warning);
} }
else else
{ {
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
$"Inherited collection {subCollectionName} for {collection.AnonymizedName} does not exist, it was removed.", $"Inherited collection {subCollectionName} for {collection.Identity.AnonymizedName} does not exist, it was removed.",
NotificationType.Warning); NotificationType.Warning);
changes = true; changes = true;
} }

View file

@ -44,7 +44,7 @@ public class TempCollectionManager : IDisposable, IService
=> _customCollections.Values; => _customCollections.Values;
public bool CollectionByName(string name, [NotNullWhen(true)] out ModCollection? collection) public bool CollectionByName(string name, [NotNullWhen(true)] out ModCollection? collection)
=> _customCollections.Values.FindFirst(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase), out collection); => _customCollections.Values.FindFirst(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase), out collection);
public bool CollectionById(Guid id, [NotNullWhen(true)] out ModCollection? collection) public bool CollectionById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
=> _customCollections.TryGetValue(id, out collection); => _customCollections.TryGetValue(id, out collection);
@ -54,12 +54,12 @@ public class TempCollectionManager : IDisposable, IService
if (GlobalChangeCounter == int.MaxValue) if (GlobalChangeCounter == int.MaxValue)
GlobalChangeCounter = 0; GlobalChangeCounter = 0;
var collection = _storage.CreateTemporary(name, ~Count, GlobalChangeCounter++); var collection = _storage.CreateTemporary(name, ~Count, GlobalChangeCounter++);
Penumbra.Log.Debug($"Creating temporary collection {collection.Name} with {collection.Id}."); Penumbra.Log.Debug($"Creating temporary collection {collection.Identity.Name} with {collection.Identity.Id}.");
if (_customCollections.TryAdd(collection.Id, collection)) if (_customCollections.TryAdd(collection.Identity.Id, collection))
{ {
// Temporary collection created. // Temporary collection created.
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty); _communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
return collection.Id; return collection.Identity.Id;
} }
return Guid.Empty; return Guid.Empty;
@ -74,7 +74,7 @@ public class TempCollectionManager : IDisposable, IService
} }
_storage.Delete(collection); _storage.Delete(collection);
Penumbra.Log.Debug($"Deleted temporary collection {collection.Id}."); Penumbra.Log.Debug($"Deleted temporary collection {collection.Identity.Id}.");
GlobalChangeCounter += Math.Max(collection.Counters.Change + 1 - GlobalChangeCounter, 0); GlobalChangeCounter += Math.Max(collection.Counters.Change + 1 - GlobalChangeCounter, 0);
for (var i = 0; i < Collections.Count; ++i) for (var i = 0; i < Collections.Count; ++i)
{ {
@ -83,7 +83,7 @@ public class TempCollectionManager : IDisposable, IService
// Temporary collection assignment removed. // Temporary collection assignment removed.
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName); _communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Id} from {Collections[i].DisplayName}."); Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Identity.Id} from {Collections[i].DisplayName}.");
Collections.Delete(i--); Collections.Delete(i--);
} }
@ -96,7 +96,7 @@ public class TempCollectionManager : IDisposable, IService
return false; return false;
// Temporary collection assignment added. // Temporary collection assignment added.
Penumbra.Log.Verbose($"Assigned temporary collection {collection.AnonymizedName} to {Collections.Last().DisplayName}."); Penumbra.Log.Verbose($"Assigned temporary collection {collection.Identity.AnonymizedName} to {Collections.Last().DisplayName}.");
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName); _communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, Collections.Last().DisplayName);
return true; return true;
} }
@ -127,6 +127,6 @@ public class TempCollectionManager : IDisposable, IService
return false; return false;
var identifier = _actors.CreatePlayer(byteString, worldId); var identifier = _actors.CreatePlayer(byteString, worldId);
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Id); return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Identity.Id);
} }
} }

View file

@ -13,42 +13,21 @@ namespace Penumbra.Collections;
/// - Index is the collections index in the ModCollection.Manager /// - Index is the collections index in the ModCollection.Manager
/// - Settings has the same size as ModManager.Mods. /// - Settings has the same size as ModManager.Mods.
/// - any change in settings or inheritance of the collection causes a Save. /// - any change in settings or inheritance of the collection causes a Save.
/// - the name can not contain invalid path characters and has to be unique when lower-cased.
/// </summary> /// </summary>
public partial class ModCollection public partial class ModCollection
{ {
public const int CurrentVersion = 2; public const int CurrentVersion = 2;
public const string DefaultCollectionName = "Default";
public const string EmptyCollectionName = "None";
/// <summary> /// <summary>
/// Create the always available Empty Collection that will always sit at index 0, /// Create the always available Empty Collection that will always sit at index 0,
/// can not be deleted and does never create a cache. /// can not be deleted and does never create a cache.
/// </summary> /// </summary>
public static readonly ModCollection Empty = new(Guid.Empty, EmptyCollectionName, LocalCollectionId.Zero, 0, 0, CurrentVersion, [], [], []); public static readonly ModCollection Empty = new(ModCollectionIdentity.Empty, 0, CurrentVersion, [], [], []);
/// <summary> The name of a collection. </summary> public ModCollectionIdentity Identity;
public string Name { get; set; }
public Guid Id { get; }
public LocalCollectionId LocalId { get; }
public string Identifier
=> Id.ToString();
public string ShortIdentifier
=> Identifier[..8];
public override string ToString() public override string ToString()
=> Name.Length > 0 ? Name : ShortIdentifier; => Identity.ToString();
/// <summary> Get the first two letters of a collection name and its Index (or None if it is the empty collection). </summary>
public string AnonymizedName
=> this == Empty ? Empty.Name : Name == DefaultCollectionName ? Name : ShortIdentifier;
/// <summary> The index of the collection is set and kept up-to-date by the CollectionManager. </summary>
public int Index { get; internal set; }
public CollectionCounters Counters; public CollectionCounters Counters;
@ -90,7 +69,7 @@ public partial class ModCollection
{ {
get get
{ {
if (Index <= 0) if (Identity.Index <= 0)
return (ModSettings.Empty, this); return (ModSettings.Empty, this);
foreach (var collection in GetFlattenedInheritance()) foreach (var collection in GetFlattenedInheritance())
@ -114,17 +93,17 @@ public partial class ModCollection
public ModCollection Duplicate(string name, LocalCollectionId localId, int index) public ModCollection Duplicate(string name, LocalCollectionId localId, int index)
{ {
Debug.Assert(index > 0, "Collection duplicated with non-positive index."); Debug.Assert(index > 0, "Collection duplicated with non-positive index.");
return new ModCollection(Guid.NewGuid(), name, localId, index, 0, CurrentVersion, Settings.Select(s => s?.DeepCopy()).ToList(), return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
[.. DirectlyInheritsFrom], UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy())); Settings.Select(s => s?.DeepCopy()).ToList(), [.. DirectlyInheritsFrom],
UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()));
} }
/// <summary> Constructor for reading from files. </summary> /// <summary> Constructor for reading from files. </summary>
public static ModCollection CreateFromData(SaveService saver, ModStorage mods, Guid id, string name, LocalCollectionId localId, int version, public static ModCollection CreateFromData(SaveService saver, ModStorage mods, ModCollectionIdentity identity, int version,
int index,
Dictionary<string, ModSettings.SavedSettings> allSettings, IReadOnlyList<string> inheritances) Dictionary<string, ModSettings.SavedSettings> allSettings, IReadOnlyList<string> inheritances)
{ {
Debug.Assert(index > 0, "Collection read with non-positive index."); Debug.Assert(identity.Index > 0, "Collection read with non-positive index.");
var ret = new ModCollection(id, name, localId, index, 0, version, [], [], allSettings) var ret = new ModCollection(identity, 0, version, [], [], allSettings)
{ {
InheritanceByName = inheritances, InheritanceByName = inheritances,
}; };
@ -137,7 +116,7 @@ public partial class ModCollection
public static ModCollection CreateTemporary(string name, LocalCollectionId localId, int index, int changeCounter) public static ModCollection CreateTemporary(string name, LocalCollectionId localId, int index, int changeCounter)
{ {
Debug.Assert(index < 0, "Temporary collection created with non-negative index."); Debug.Assert(index < 0, "Temporary collection created with non-negative index.");
var ret = new ModCollection(Guid.NewGuid(), name, localId, index, changeCounter, CurrentVersion, [], [], []); var ret = new ModCollection(ModCollectionIdentity.New(name, localId, index), changeCounter, CurrentVersion, [], [], []);
return ret; return ret;
} }
@ -145,9 +124,8 @@ public partial class ModCollection
public static ModCollection CreateEmpty(string name, LocalCollectionId localId, int index, int modCount) public static ModCollection CreateEmpty(string name, LocalCollectionId localId, int index, int modCount)
{ {
Debug.Assert(index >= 0, "Empty collection created with negative index."); Debug.Assert(index >= 0, "Empty collection created with negative index.");
return new ModCollection(Guid.NewGuid(), name, localId, index, 0, CurrentVersion, return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
Enumerable.Repeat((ModSettings?)null, modCount).ToList(), [], Enumerable.Repeat((ModSettings?)null, modCount).ToList(), [], []);
[]);
} }
/// <summary> Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. </summary> /// <summary> Add settings for a new appended mod, by checking if the mod had settings from a previous deletion. </summary>
@ -195,16 +173,13 @@ public partial class ModCollection
saver.ImmediateSave(new ModCollectionSave(mods, this)); saver.ImmediateSave(new ModCollectionSave(mods, this));
} }
private ModCollection(Guid id, string name, LocalCollectionId localId, int index, int changeCounter, int version, private ModCollection(ModCollectionIdentity identity, int changeCounter, int version, List<ModSettings?> appliedSettings,
List<ModSettings?> appliedSettings, List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings) List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings)
{ {
Name = name; Identity = identity;
Id = id;
LocalId = localId;
Index = index;
Counters = new CollectionCounters(changeCounter); Counters = new CollectionCounters(changeCounter);
Settings = appliedSettings; Settings = appliedSettings;
UnusedSettings = settings; UnusedSettings = settings;
DirectlyInheritsFrom = inheritsFrom; DirectlyInheritsFrom = inheritsFrom;
foreach (var c in DirectlyInheritsFrom) foreach (var c in DirectlyInheritsFrom)
((List<ModCollection>)c.DirectParentOf).Add(this); ((List<ModCollection>)c.DirectParentOf).Add(this);

View file

@ -0,0 +1,42 @@
using OtterGui;
using Penumbra.Collections.Manager;
namespace Penumbra.Collections;
public struct ModCollectionIdentity(Guid id, LocalCollectionId localId)
{
public const string DefaultCollectionName = "Default";
public const string EmptyCollectionName = "None";
public static readonly ModCollectionIdentity Empty = new(Guid.Empty, LocalCollectionId.Zero, EmptyCollectionName, 0);
public string Name { get; set; }
public Guid Id { get; } = id;
public LocalCollectionId LocalId { get; } = localId;
/// <summary> The index of the collection is set and kept up-to-date by the CollectionManager. </summary>
public int Index { get; internal set; }
public string Identifier
=> Id.ToString();
public string ShortIdentifier
=> Id.ShortGuid();
/// <summary> Get the short identifier of a collection unless it is a well-known collection name. </summary>
public string AnonymizedName
=> Id == Guid.Empty ? EmptyCollectionName : Name == DefaultCollectionName ? Name : ShortIdentifier;
public override string ToString()
=> Name.Length > 0 ? Name : ShortIdentifier;
public ModCollectionIdentity(Guid id, LocalCollectionId localId, string name, int index)
: this(id, localId)
{
Name = name;
Index = index;
}
public static ModCollectionIdentity New(string name, LocalCollectionId id, int index)
=> new(Guid.NewGuid(), id, name, index);
}

View file

@ -15,7 +15,7 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
=> fileNames.CollectionFile(modCollection); => fileNames.CollectionFile(modCollection);
public string LogName(string _) public string LogName(string _)
=> modCollection.AnonymizedName; => modCollection.Identity.AnonymizedName;
public string TypeName public string TypeName
=> "Collection"; => "Collection";
@ -28,10 +28,10 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
j.WriteStartObject(); j.WriteStartObject();
j.WritePropertyName("Version"); j.WritePropertyName("Version");
j.WriteValue(ModCollection.CurrentVersion); j.WriteValue(ModCollection.CurrentVersion);
j.WritePropertyName(nameof(ModCollection.Id)); j.WritePropertyName(nameof(ModCollectionIdentity.Id));
j.WriteValue(modCollection.Identifier); j.WriteValue(modCollection.Identity.Identifier);
j.WritePropertyName(nameof(ModCollection.Name)); j.WritePropertyName(nameof(ModCollectionIdentity.Name));
j.WriteValue(modCollection.Name); j.WriteValue(modCollection.Identity.Name);
j.WritePropertyName(nameof(ModCollection.Settings)); j.WritePropertyName(nameof(ModCollection.Settings));
// Write all used and unused settings by mod directory name. // Write all used and unused settings by mod directory name.
@ -57,7 +57,7 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
// Inherit by collection name. // Inherit by collection name.
j.WritePropertyName("Inheritance"); j.WritePropertyName("Inheritance");
x.Serialize(j, modCollection.InheritanceByName ?? modCollection.DirectlyInheritsFrom.Select(c => c.Identifier)); x.Serialize(j, modCollection.InheritanceByName ?? modCollection.DirectlyInheritsFrom.Select(c => c.Identity.Identifier));
j.WriteEndObject(); j.WriteEndObject();
} }
@ -79,8 +79,8 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
{ {
var obj = JObject.Parse(File.ReadAllText(file.FullName)); var obj = JObject.Parse(File.ReadAllText(file.FullName));
version = obj["Version"]?.ToObject<int>() ?? 0; version = obj["Version"]?.ToObject<int>() ?? 0;
name = obj[nameof(ModCollection.Name)]?.ToObject<string>() ?? string.Empty; name = obj[nameof(ModCollectionIdentity.Name)]?.ToObject<string>() ?? string.Empty;
id = obj[nameof(ModCollection.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty); id = obj[nameof(ModCollectionIdentity.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty);
// Custom deserialization that is converted with the constructor. // Custom deserialization that is converted with the constructor.
settings = obj[nameof(ModCollection.Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>() ?? settings; settings = obj[nameof(ModCollection.Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>() ?? settings;
inheritance = obj["Inheritance"]?.ToObject<List<string>>() ?? inheritance; inheritance = obj["Inheritance"]?.ToObject<List<string>>() ?? inheritance;

View file

@ -23,7 +23,7 @@ public readonly struct ResolveData(ModCollection collection, nint gameObject)
{ } { }
public override string ToString() public override string ToString()
=> ModCollection.Name; => ModCollection.Identity.Name;
} }
public static class ResolveDataExtensions public static class ResolveDataExtensions

View file

@ -326,7 +326,7 @@ public class CommandHandler : IDisposable, IApiService
{ {
_chat.Print(collection == null _chat.Print(collection == null
? $"The {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}" : string.Empty)} is already unassigned" ? $"The {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}" : string.Empty)} is already unassigned"
: $"{collection.Name} already is the {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}"); : $"{collection.Identity.Name} already is the {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
continue; continue;
} }
@ -363,13 +363,13 @@ public class CommandHandler : IDisposable, IApiService
} }
Print( Print(
$"Removed {oldCollection.Name} as {type.ToName()} Collection assignment {(identifier.IsValid ? $" for {identifier}." : ".")}"); $"Removed {oldCollection.Identity.Name} as {type.ToName()} Collection assignment {(identifier.IsValid ? $" for {identifier}." : ".")}");
anySuccess = true; anySuccess = true;
continue; continue;
} }
_collectionManager.Active.SetCollection(collection!, type, individualIndex); _collectionManager.Active.SetCollection(collection!, type, individualIndex);
Print($"Assigned {collection!.Name} as {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}"); Print($"Assigned {collection!.Identity.Name} as {type.ToName()} Collection{(identifier.IsValid ? $" for {identifier}." : ".")}");
} }
return anySuccess; return anySuccess;
@ -440,7 +440,7 @@ public class CommandHandler : IDisposable, IApiService
_chat.Print(new SeStringBuilder().AddText("Mod ").AddPurple(mod.Name, true) _chat.Print(new SeStringBuilder().AddText("Mod ").AddPurple(mod.Name, true)
.AddText("already had the desired state in collection ") .AddText("already had the desired state in collection ")
.AddYellow(collection!.Name, true).AddText(".").BuiltString); .AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
return false; return false;
} }
@ -458,7 +458,7 @@ public class CommandHandler : IDisposable, IApiService
_collectionEditor.SetModSetting(collection!, mod, groupIndex, setting); _collectionEditor.SetModSetting(collection!, mod, groupIndex, setting);
Print(() => new SeStringBuilder().AddText("Changed settings of group ").AddGreen(groupName, true).AddText(" in mod ") Print(() => new SeStringBuilder().AddText("Changed settings of group ").AddGreen(groupName, true).AddText(" in mod ")
.AddPurple(mod.Name, true).AddText(" in collection ") .AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection!.Name, true).AddText(".").BuiltString); .AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
return true; return true;
} }
@ -543,7 +543,7 @@ public class CommandHandler : IDisposable, IApiService
changes |= HandleModState(state, collection!, mod); changes |= HandleModState(state, collection!, mod);
if (!changes) if (!changes)
Print(() => new SeStringBuilder().AddText("No mod states were changed in collection ").AddYellow(collection!.Name, true) Print(() => new SeStringBuilder().AddText("No mod states were changed in collection ").AddYellow(collection!.Identity.Name, true)
.AddText(".").BuiltString); .AddText(".").BuiltString);
return true; return true;
@ -558,7 +558,7 @@ public class CommandHandler : IDisposable, IApiService
return true; return true;
} }
collection = string.Equals(lowerName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase) collection = string.Equals(lowerName, ModCollection.Empty.Identity.Name, StringComparison.OrdinalIgnoreCase)
? ModCollection.Empty ? ModCollection.Empty
: _collectionManager.Storage.ByIdentifier(lowerName, out var c) : _collectionManager.Storage.ByIdentifier(lowerName, out var c)
? c ? c
@ -614,7 +614,7 @@ public class CommandHandler : IDisposable, IApiService
return false; return false;
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ") Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true) .AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString); .AddText(".").BuiltString);
return true; return true;
@ -623,7 +623,7 @@ public class CommandHandler : IDisposable, IApiService
return false; return false;
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ") Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true) .AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString); .AddText(".").BuiltString);
return true; return true;
@ -634,7 +634,7 @@ public class CommandHandler : IDisposable, IApiService
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true) Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
.AddText(" in collection ") .AddText(" in collection ")
.AddYellow(collection.Name, true) .AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString); .AddText(".").BuiltString);
return true; return true;
@ -643,7 +643,7 @@ public class CommandHandler : IDisposable, IApiService
return false; return false;
Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ") Print(() => new SeStringBuilder().AddText("Set mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true) .AddYellow(collection.Identity.Name, true)
.AddText(" to inherit.").BuiltString); .AddText(" to inherit.").BuiltString);
return true; return true;
} }

View file

@ -30,7 +30,7 @@ public unsafe class AtchCallerHook1 : FastHook<AtchCallerHook1.Delegate>, IDispo
Task.Result.Original(data, slot, unk, playerModel); Task.Result.Original(data, slot, unk, playerModel);
_metaState.AtchCollection.Pop(); _metaState.AtchCollection.Pop();
Penumbra.Log.Excessive( Penumbra.Log.Excessive(
$"[AtchCaller1] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, identified to {collection.ModCollection.AnonymizedName}."); $"[AtchCaller1] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, identified to {collection.ModCollection.Identity.AnonymizedName}.");
} }
public void Dispose() public void Dispose()

View file

@ -30,7 +30,7 @@ public unsafe class AtchCallerHook2 : FastHook<AtchCallerHook2.Delegate>, IDispo
Task.Result.Original(data, slot, unk, playerModel, unk2); Task.Result.Original(data, slot, unk, playerModel, unk2);
_metaState.AtchCollection.Pop(); _metaState.AtchCollection.Pop();
Penumbra.Log.Excessive( Penumbra.Log.Excessive(
$"[AtchCaller2] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, {unk2}, identified to {collection.ModCollection.AnonymizedName}."); $"[AtchCaller2] Invoked on 0x{(ulong)data:X} with {slot}, {unk:X}, 0x{playerModel.Address:X}, {unk2}, identified to {collection.ModCollection.Identity.AnonymizedName}.");
} }
public void Dispose() public void Dispose()

View file

@ -98,7 +98,7 @@ public sealed unsafe class MetaState : IDisposable, IService
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true); _lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero) if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
_communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject, _communicator.CreatingCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject,
_lastCreatedCollection.ModCollection.Id, (nint)modelCharaId, (nint)customize, (nint)equipData); _lastCreatedCollection.ModCollection.Identity.Id, (nint)modelCharaId, (nint)customize, (nint)equipData);
var decal = new DecalReverter(Config, _characterUtility, _resources, _lastCreatedCollection, var decal = new DecalReverter(Config, _characterUtility, _resources, _lastCreatedCollection,
UsesDecal(*(uint*)modelCharaId, (nint)customize)); UsesDecal(*(uint*)modelCharaId, (nint)customize));

View file

@ -32,7 +32,7 @@ public static class PathDataHandler
/// <summary> Create the encoding path for an IMC file. </summary> /// <summary> Create the encoding path for an IMC file. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FullPath CreateImc(CiByteString path, ModCollection collection) public static FullPath CreateImc(CiByteString path, ModCollection collection)
=> new($"|{collection.LocalId.Id}_{collection.Counters.Imc}_{DiscriminatorString}|{path}"); => new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Imc}_{DiscriminatorString}|{path}");
/// <summary> Create the encoding path for a TMB file. </summary> /// <summary> Create the encoding path for a TMB file. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -47,17 +47,17 @@ public static class PathDataHandler
/// <summary> Create the encoding path for an ATCH file. </summary> /// <summary> Create the encoding path for an ATCH file. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FullPath CreateAtch(CiByteString path, ModCollection collection) public static FullPath CreateAtch(CiByteString path, ModCollection collection)
=> new($"|{collection.LocalId.Id}_{collection.Counters.Atch}_{DiscriminatorString}|{path}"); => new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Atch}_{DiscriminatorString}|{path}");
/// <summary> Create the encoding path for a MTRL file. </summary> /// <summary> Create the encoding path for a MTRL file. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FullPath CreateMtrl(CiByteString path, ModCollection collection, Utf8GamePath originalPath) public static FullPath CreateMtrl(CiByteString path, ModCollection collection, Utf8GamePath originalPath)
=> new($"|{collection.LocalId.Id}_{collection.Counters.Change}_{originalPath.Path.Crc32:X8}_{DiscriminatorString}|{path}"); => new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Change}_{originalPath.Path.Crc32:X8}_{DiscriminatorString}|{path}");
/// <summary> The base function shared by most file types. </summary> /// <summary> The base function shared by most file types. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static FullPath CreateBase(CiByteString path, ModCollection collection) private static FullPath CreateBase(CiByteString path, ModCollection collection)
=> new($"|{collection.LocalId.Id}_{collection.Counters.Change}_{DiscriminatorString}|{path}"); => new($"|{collection.Identity.LocalId.Id}_{collection.Counters.Change}_{DiscriminatorString}|{path}");
/// <summary> Read an additional data blurb and parse it into usable data for all file types but Materials. </summary> /// <summary> Read an additional data blurb and parse it into usable data for all file types but Materials. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View file

@ -26,6 +26,6 @@ public sealed class ImcFilePostProcessor(CollectionStorage collections) : IFileP
file.Replace(resource); file.Replace(resource);
Penumbra.Log.Verbose( Penumbra.Log.Verbose(
$"[ResourceLoader] Loaded {originalGamePath} from file and replaced with IMC from collection {collection.AnonymizedName}."); $"[ResourceLoader] Loaded {originalGamePath} from file and replaced with IMC from collection {collection.Identity.AnonymizedName}.");
} }
} }

View file

@ -81,7 +81,7 @@ public class ResourceTreeFactory(
var (name, anonymizedName, related) = GetCharacterName(character); var (name, anonymizedName, related) = GetCharacterName(character);
var networked = character.EntityId != 0xE0000000; var networked = character.EntityId != 0xE0000000;
var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
networked, collectionResolveData.ModCollection.Name, collectionResolveData.ModCollection.AnonymizedName); networked, collectionResolveData.ModCollection.Identity.Name, collectionResolveData.ModCollection.Identity.AnonymizedName);
var globalContext = new GlobalResolveContext(metaFileManager, objectIdentifier, collectionResolveData.ModCollection, var globalContext = new GlobalResolveContext(metaFileManager, objectIdentifier, collectionResolveData.ModCollection,
cache, (flags & Flags.WithUiData) != 0); cache, (flags & Flags.WithUiData) != 0);
using (var _ = pathState.EnterInternalResolve()) using (var _ = pathState.EnterInternalResolve())

View file

@ -63,10 +63,10 @@ public class TemporaryMod : IMod
DirectoryInfo? dir = null; DirectoryInfo? dir = null;
try try
{ {
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true); dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Identity.Name, config.ReplaceNonAsciiOnImport, true);
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files")); var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor, modManager.DataEditor.CreateMeta(dir, collection.Identity.Name, character ?? config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Id} for {character ?? "Unknown Character"} with name {collection.Name}.", $"Mod generated from temporary collection {collection.Identity.Id} for {character ?? "Unknown Character"} with name {collection.Identity.Name}.",
null, null); null, null);
var mod = new Mod(dir); var mod = new Mod(dir);
var defaultMod = mod.Default; var defaultMod = mod.Default;
@ -99,11 +99,11 @@ public class TemporaryMod : IMod
saveService.ImmediateSaveSync(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport)); saveService.ImmediateSaveSync(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
modManager.AddMod(dir, false); modManager.AddMod(dir, false);
Penumbra.Log.Information( Penumbra.Log.Information(
$"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identifier}."); $"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identity.Identifier}.");
} }
catch (Exception e) catch (Exception e)
{ {
Penumbra.Log.Error($"Could not save temporary collection {collection.Identifier} to permanent Mod:\n{e}"); Penumbra.Log.Error($"Could not save temporary collection {collection.Identity.Identifier} to permanent Mod:\n{e}");
if (dir != null && Directory.Exists(dir.FullName)) if (dir != null && Directory.Exists(dir.FullName))
{ {
try try

View file

@ -245,24 +245,24 @@ public class Penumbra : IDalamudPlugin
void PrintCollection(ModCollection c, CollectionCache _) void PrintCollection(ModCollection c, CollectionCache _)
=> sb.Append( => sb.Append(
$"> **`Collection {c.AnonymizedName + ':',-18}`** Inheritances: `{c.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n"); $"> **`Collection {c.Identity.AnonymizedName + ':',-18}`** Inheritances: `{c.DirectlyInheritsFrom.Count,3}`, Enabled Mods: `{c.ActualSettings.Count(s => s is { Enabled: true }),4}`, Conflicts: `{c.AllConflicts.SelectMany(x => x).Sum(x => x is { HasPriority: true, Solved: true } ? x.Conflicts.Count : 0),5}/{c.AllConflicts.SelectMany(x => x).Sum(x => x.HasPriority ? x.Conflicts.Count : 0),5}`\n");
sb.AppendLine("**Collections**"); sb.AppendLine("**Collections**");
sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n"); sb.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n");
sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n"); sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n");
sb.Append($"> **`Active Collections: `** {_collectionManager.Caches.Count}\n"); sb.Append($"> **`Active Collections: `** {_collectionManager.Caches.Count}\n");
sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.AnonymizedName}\n"); sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.Identity.AnonymizedName}\n");
sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.AnonymizedName}\n"); sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.Identity.AnonymizedName}\n");
sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.AnonymizedName}\n"); sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.Identity.AnonymizedName}\n");
foreach (var (type, name, _) in CollectionTypeExtensions.Special) foreach (var (type, name, _) in CollectionTypeExtensions.Special)
{ {
var collection = _collectionManager.Active.ByType(type); var collection = _collectionManager.Active.ByType(type);
if (collection != null) if (collection != null)
sb.Append($"> **`{name,-29}`** {collection.AnonymizedName}\n"); sb.Append($"> **`{name,-29}`** {collection.Identity.AnonymizedName}\n");
} }
foreach (var (name, id, collection) in _collectionManager.Active.Individuals.Assignments) foreach (var (name, id, collection) in _collectionManager.Active.Individuals.Assignments)
sb.Append($"> **`{id[0].Incognito(name) + ':',-29}`** {collection.AnonymizedName}\n"); sb.Append($"> **`{id[0].Incognito(name) + ':',-29}`** {collection.Identity.AnonymizedName}\n");
foreach (var collection in _collectionManager.Caches.Active) foreach (var collection in _collectionManager.Caches.Active)
PrintCollection(collection, collection._cache!); PrintCollection(collection, collection._cache!);

View file

@ -27,8 +27,8 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
private Configuration _config = null!; private Configuration _config = null!;
private JObject _data = null!; private JObject _data = null!;
public string CurrentCollection = ModCollection.DefaultCollectionName; public string CurrentCollection = ModCollectionIdentity.DefaultCollectionName;
public string DefaultCollection = ModCollection.DefaultCollectionName; public string DefaultCollection = ModCollectionIdentity.DefaultCollectionName;
public string ForcedCollection = string.Empty; public string ForcedCollection = string.Empty;
public Dictionary<string, string> CharacterCollections = []; public Dictionary<string, string> CharacterCollections = [];
public Dictionary<string, string> ModSortOrder = []; public Dictionary<string, string> ModSortOrder = [];
@ -346,7 +346,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
if (!collectionJson.Exists) if (!collectionJson.Exists)
return; return;
var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollection.DefaultCollectionName)); var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollectionIdentity.DefaultCollectionName));
if (defaultCollectionFile.Exists) if (defaultCollectionFile.Exists)
return; return;
@ -380,7 +380,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
var emptyStorage = new ModStorage(); var emptyStorage = new ModStorage();
// Only used for saving and immediately discarded, so the local collection id here is irrelevant. // Only used for saving and immediately discarded, so the local collection id here is irrelevant.
var collection = ModCollection.CreateFromData(saveService, emptyStorage, Guid.NewGuid(), ModCollection.DefaultCollectionName, LocalCollectionId.Zero, 0, 1, dict, []); var collection = ModCollection.CreateFromData(saveService, emptyStorage, ModCollectionIdentity.New(ModCollectionIdentity.DefaultCollectionName, LocalCollectionId.Zero, 1), 0, dict, []);
saveService.ImmediateSaveSync(new ModCollectionSave(emptyStorage, collection)); saveService.ImmediateSaveSync(new ModCollectionSave(emptyStorage, collection));
} }
catch (Exception e) catch (Exception e)

View file

@ -240,7 +240,7 @@ public sealed class CrashHandlerService : IDisposable, IService
var name = GetActorName(character); var name = GetActorName(character);
lock (_eventWriter) lock (_eventWriter)
{ {
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Id, type); _eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Identity.Id, type);
} }
} }
catch (Exception ex) catch (Exception ex)
@ -293,7 +293,7 @@ public sealed class CrashHandlerService : IDisposable, IService
var name = GetActorName(resolveData.AssociatedGameObject); var name = GetActorName(resolveData.AssociatedGameObject);
lock (_eventWriter) lock (_eventWriter)
{ {
_eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Id, _eventWriter!.FileLoaded.WriteLine(resolveData.AssociatedGameObject, name.Span, resolveData.ModCollection.Identity.Id,
manipulatedPath.Value.InternalName.Span, originalPath.Path.Span); manipulatedPath.Value.InternalName.Span, originalPath.Path.Span);
} }
} }

View file

@ -24,7 +24,7 @@ public class FilenameService(IDalamudPluginInterface pi) : IService
/// <summary> Obtain the path of a collection file given its name.</summary> /// <summary> Obtain the path of a collection file given its name.</summary>
public string CollectionFile(ModCollection collection) public string CollectionFile(ModCollection collection)
=> CollectionFile(collection.Identifier); => CollectionFile(collection.Identity.Identifier);
/// <summary> Obtain the path of a collection file given its name. </summary> /// <summary> Obtain the path of a collection file given its name. </summary>
public string CollectionFile(string collectionName) public string CollectionFile(string collectionName)

View file

@ -25,7 +25,7 @@ public class CollectionSelectHeader : IUiService
_selection = selection; _selection = selection;
_resolver = resolver; _resolver = resolver;
_activeCollections = collectionManager.Active; _activeCollections = collectionManager.Active;
_collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Name).ToList()); _collectionCombo = new CollectionCombo(collectionManager, () => collectionManager.Storage.OrderBy(c => c.Identity.Name).ToList());
} }
/// <summary> Draw the header line that can quick switch between collections. </summary> /// <summary> Draw the header line that can quick switch between collections. </summary>
@ -77,10 +77,10 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch return CheckCollection(collection) switch
{ {
CollectionState.Empty => (collection, "None", "The base collection is configured to use no mods.", true), CollectionState.Empty => (collection, "None", "The base collection is configured to use no mods.", true),
CollectionState.Selected => (collection, collection.Name, CollectionState.Selected => (collection, collection.Identity.Name,
"The configured base collection is already selected as the current collection.", true), "The configured base collection is already selected as the current collection.", true),
CollectionState.Available => (collection, collection.Name, CollectionState.Available => (collection, collection.Identity.Name,
$"Select the configured base collection {collection.Name} as the current collection.", false), $"Select the configured base collection {collection.Identity.Name} as the current collection.", false),
_ => throw new Exception("Can not happen."), _ => throw new Exception("Can not happen."),
}; };
} }
@ -91,10 +91,11 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch return CheckCollection(collection) switch
{ {
CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true), CollectionState.Empty => (collection, "None", "The loaded player character is configured to use no mods.", true),
CollectionState.Selected => (collection, collection.Name, CollectionState.Selected => (collection, collection.Identity.Name,
"The collection configured to apply to the loaded player character is already selected as the current collection.", true), "The collection configured to apply to the loaded player character is already selected as the current collection.", true),
CollectionState.Available => (collection, collection.Name, CollectionState.Available => (collection, collection.Identity.Name,
$"Select the collection {collection.Name} that applies to the loaded player character as the current collection.", false), $"Select the collection {collection.Identity.Name} that applies to the loaded player character as the current collection.",
false),
_ => throw new Exception("Can not happen."), _ => throw new Exception("Can not happen."),
}; };
} }
@ -105,10 +106,10 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch return CheckCollection(collection) switch
{ {
CollectionState.Empty => (collection, "None", "The interface collection is configured to use no mods.", true), CollectionState.Empty => (collection, "None", "The interface collection is configured to use no mods.", true),
CollectionState.Selected => (collection, collection.Name, CollectionState.Selected => (collection, collection.Identity.Name,
"The configured interface collection is already selected as the current collection.", true), "The configured interface collection is already selected as the current collection.", true),
CollectionState.Available => (collection, collection.Name, CollectionState.Available => (collection, collection.Identity.Name,
$"Select the configured interface collection {collection.Name} as the current collection.", false), $"Select the configured interface collection {collection.Identity.Name} as the current collection.", false),
_ => throw new Exception("Can not happen."), _ => throw new Exception("Can not happen."),
}; };
} }
@ -120,8 +121,8 @@ public class CollectionSelectHeader : IUiService
{ {
CollectionState.Unavailable => (null, "Not Inherited", CollectionState.Unavailable => (null, "Not Inherited",
"The settings of the selected mod are not inherited from another collection.", true), "The settings of the selected mod are not inherited from another collection.", true),
CollectionState.Available => (collection, collection!.Name, CollectionState.Available => (collection, collection!.Identity.Name,
$"Select the collection {collection!.Name} from which the selected mod inherits its settings as the current collection.", $"Select the collection {collection!.Identity.Name} from which the selected mod inherits its settings as the current collection.",
false), false),
_ => throw new Exception("Can not happen."), _ => throw new Exception("Can not happen."),
}; };

View file

@ -29,13 +29,13 @@ public sealed class CollectionCombo(CollectionManager manager, Func<IReadOnlyLis
} }
_color.Push(ImGuiCol.FrameBg, color).Push(ImGuiCol.FrameBgHovered, color); _color.Push(ImGuiCol.FrameBg, color).Push(ImGuiCol.FrameBgHovered, color);
if (Draw(label, current.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null) if (Draw(label, current.Identity.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing()) && CurrentSelection != null)
manager.Active.SetCollection(CurrentSelection, CollectionType.Current); manager.Active.SetCollection(CurrentSelection, CollectionType.Current);
_color.Dispose(); _color.Dispose();
} }
protected override string ToString(ModCollection obj) protected override string ToString(ModCollection obj)
=> obj.Name; => obj.Identity.Name;
protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight, protected override void DrawCombo(string label, string preview, string tooltip, int currentSelected, float previewWidth, float itemHeight,
ImGuiComboFlags flags) ImGuiComboFlags flags)

View file

@ -221,16 +221,16 @@ public sealed class CollectionPanel(
ImGui.SameLine(); ImGui.SameLine();
ImGui.BeginGroup(); ImGui.BeginGroup();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f)); using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
var name = _newName ?? collection.Name; var name = _newName ?? collection.Identity.Name;
var identifier = collection.Identifier; var identifier = collection.Identity.Identifier;
var width = ImGui.GetContentRegionAvail().X; var width = ImGui.GetContentRegionAvail().X;
var fileName = saveService.FileNames.CollectionFile(collection); var fileName = saveService.FileNames.CollectionFile(collection);
ImGui.SetNextItemWidth(width); ImGui.SetNextItemWidth(width);
if (ImGui.InputText("##name", ref name, 128)) if (ImGui.InputText("##name", ref name, 128))
_newName = name; _newName = name;
if (ImGui.IsItemDeactivatedAfterEdit() && _newName != null && _newName != collection.Name) if (ImGui.IsItemDeactivatedAfterEdit() && _newName != null && _newName != collection.Identity.Name)
{ {
collection.Name = _newName; collection.Identity.Name = _newName;
saveService.QueueSave(new ModCollectionSave(mods, collection)); saveService.QueueSave(new ModCollectionSave(mods, collection));
selector.RestoreCollections(); selector.RestoreCollections();
_newName = null; _newName = null;
@ -242,7 +242,7 @@ public sealed class CollectionPanel(
using (ImRaii.PushFont(UiBuilder.MonoFont)) using (ImRaii.PushFont(UiBuilder.MonoFont))
{ {
if (ImGui.Button(collection.Identifier, new Vector2(width, 0))) if (ImGui.Button(collection.Identity.Identifier, new Vector2(width, 0)))
try try
{ {
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true }); Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
@ -289,9 +289,9 @@ public sealed class CollectionPanel(
_active.SetCollection(null, type, _active.Individuals.GetGroup(identifier)); _active.SetCollection(null, type, _active.Individuals.GetGroup(identifier));
} }
foreach (var coll in _collections.OrderBy(c => c.Name)) foreach (var coll in _collections.OrderBy(c => c.Identity.Name))
{ {
if (coll != collection && ImGui.MenuItem($"Use {coll.Name}.")) if (coll != collection && ImGui.MenuItem($"Use {coll.Identity.Name}."))
_active.SetCollection(coll, type, _active.Individuals.GetGroup(identifier)); _active.SetCollection(coll, type, _active.Individuals.GetGroup(identifier));
} }
} }
@ -418,7 +418,7 @@ public sealed class CollectionPanel(
private string Name(ModCollection? collection) private string Name(ModCollection? collection)
=> collection == null ? "Unassigned" : => collection == null ? "Unassigned" :
collection == ModCollection.Empty ? "Use No Mods" : collection == ModCollection.Empty ? "Use No Mods" :
incognito.IncognitoMode ? collection.AnonymizedName : collection.Name; incognito.IncognitoMode ? collection.Identity.AnonymizedName : collection.Identity.Name;
private void DrawIndividualButton(string intro, Vector2 width, string tooltip, char suffix, params ActorIdentifier[] identifiers) private void DrawIndividualButton(string intro, Vector2 width, string tooltip, char suffix, params ActorIdentifier[] identifiers)
{ {

View file

@ -69,7 +69,7 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
} }
protected override bool Filtered(int idx) protected override bool Filtered(int idx)
=> !Items[idx].Name.Contains(Filter, StringComparison.OrdinalIgnoreCase); => !Items[idx].Identity.Name.Contains(Filter, StringComparison.OrdinalIgnoreCase);
private const string PayloadString = "Collection"; private const string PayloadString = "Collection";
@ -111,12 +111,12 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
} }
private string Name(ModCollection collection) private string Name(ModCollection collection)
=> _incognito.IncognitoMode || collection.Name.Length == 0 ? collection.AnonymizedName : collection.Name; => _incognito.IncognitoMode || collection.Identity.Name.Length == 0 ? collection.Identity.AnonymizedName : collection.Identity.Name;
public void RestoreCollections() public void RestoreCollections()
{ {
Items.Clear(); Items.Clear();
foreach (var c in _storage.OrderBy(c => c.Name)) foreach (var c in _storage.OrderBy(c => c.Identity.Name))
Items.Add(c); Items.Add(c);
SetFilterDirty(); SetFilterDirty();
SetCurrent(_active.Current); SetCurrent(_active.Current);

View file

@ -93,7 +93,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
/// </summary> /// </summary>
private void DrawInheritedChildren(ModCollection collection) private void DrawInheritedChildren(ModCollection collection)
{ {
using var id = ImRaii.PushId(collection.Index); using var id = ImRaii.PushId(collection.Identity.Index);
using var indent = ImRaii.PushIndent(); using var indent = ImRaii.PushIndent();
// Get start point for the lines (top of the selector). // Get start point for the lines (top of the selector).
@ -114,7 +114,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
_seenInheritedCollections.Contains(inheritance)); _seenInheritedCollections.Contains(inheritance));
_seenInheritedCollections.Add(inheritance); _seenInheritedCollections.Add(inheritance);
ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Id}", ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Identity.Id}",
ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet); ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
var (minRect, maxRect) = (ImGui.GetItemRectMin(), ImGui.GetItemRectMax()); var (minRect, maxRect) = (ImGui.GetItemRectMin(), ImGui.GetItemRectMax());
DrawInheritanceTreeClicks(inheritance, false); DrawInheritanceTreeClicks(inheritance, false);
@ -140,7 +140,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(), using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
_seenInheritedCollections.Contains(collection)); _seenInheritedCollections.Contains(collection));
_seenInheritedCollections.Add(collection); _seenInheritedCollections.Add(collection);
using var tree = ImRaii.TreeNode($"{Name(collection)}###{collection.Name}", ImGuiTreeNodeFlags.NoTreePushOnOpen); using var tree = ImRaii.TreeNode($"{Name(collection)}###{collection.Identity.Name}", ImGuiTreeNodeFlags.NoTreePushOnOpen);
color.Pop(); color.Pop();
DrawInheritanceTreeClicks(collection, true); DrawInheritanceTreeClicks(collection, true);
DrawInheritanceDropSource(collection); DrawInheritanceDropSource(collection);
@ -252,7 +252,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
foreach (var collection in _collections foreach (var collection in _collections
.Where(c => InheritanceManager.CheckValidInheritance(_active.Current, c) == InheritanceManager.ValidInheritance.Valid) .Where(c => InheritanceManager.CheckValidInheritance(_active.Current, c) == InheritanceManager.ValidInheritance.Valid)
.OrderBy(c => c.Name)) .OrderBy(c => c.Identity.Name))
{ {
if (ImGui.Selectable(Name(collection), _newInheritance == collection)) if (ImGui.Selectable(Name(collection), _newInheritance == collection))
_newInheritance = collection; _newInheritance = collection;
@ -312,5 +312,5 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
} }
private string Name(ModCollection collection) private string Name(ModCollection collection)
=> incognito.IncognitoMode ? collection.AnonymizedName : collection.Name; => incognito.IncognitoMode ? collection.Identity.AnonymizedName : collection.Identity.Name;
} }

View file

@ -56,7 +56,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
foreach (var ((collection, parent, color, state), idx) in _cache.WithIndex()) foreach (var ((collection, parent, color, state), idx) in _cache.WithIndex())
{ {
using var id = ImUtf8.PushId(idx); using var id = ImUtf8.PushId(idx);
ImUtf8.DrawTableColumn(collection.Name); ImUtf8.DrawTableColumn(collection.Identity.Name);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImUtf8.Text(ToText(state), color); ImUtf8.Text(ToText(state), color);
@ -65,7 +65,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
{ {
if (context) if (context)
{ {
ImUtf8.Text(collection.Name); ImUtf8.Text(collection.Identity.Name);
ImGui.Separator(); ImGui.Separator();
using (ImRaii.Disabled(state is ModState.Enabled && parent == collection)) using (ImRaii.Disabled(state is ModState.Enabled && parent == collection))
{ {
@ -95,7 +95,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
} }
} }
ImUtf8.DrawTableColumn(parent == collection ? string.Empty : parent.Name); ImUtf8.DrawTableColumn(parent == collection ? string.Empty : parent.Identity.Name);
} }
} }

View file

@ -67,7 +67,7 @@ public class ModPanelSettingsTab(
using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg); using var color = ImRaii.PushColor(ImGuiCol.Button, Colors.PressEnterWarningBg);
var width = new Vector2(ImGui.GetContentRegionAvail().X, 0); var width = new Vector2(ImGui.GetContentRegionAvail().X, 0);
if (ImGui.Button($"These settings are inherited from {selection.Collection.Name}.", width)) if (ImGui.Button($"These settings are inherited from {selection.Collection.Identity.Name}.", width))
collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selection.Mod!, false); collectionManager.Editor.SetModInheritance(collectionManager.Active.Current, selection.Mod!, false);
ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n" ImGuiUtil.HoverTooltip("You can click this button to copy the current settings to the current selection.\n"

View file

@ -241,7 +241,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService
{ {
var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name; var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name;
Penumbra.Log.Information( Penumbra.Log.Information(
$"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.AnonymizedName} for {Name(data, "no associated object.")} (Refcount {handle->RefCount}) "); $"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.Identity.AnonymizedName} for {Name(data, "no associated object.")} (Refcount {handle->RefCount}) ");
} }
} }

View file

@ -167,7 +167,7 @@ internal sealed class ResourceWatcherTable : Table<Record>
=> 80 * UiHelpers.Scale; => 80 * UiHelpers.Scale;
public override string ToName(Record item) public override string ToName(Record item)
=> item.Collection?.Name ?? string.Empty; => (item.Collection != null ? item.Collection.Identity.Name : null) ?? string.Empty;
} }
private sealed class ObjectColumn : ColumnString<Record> private sealed class ObjectColumn : ColumnString<Record>

View file

@ -204,7 +204,7 @@ public class DebugTab : Window, ITab, IUiService
if (collection.HasCache) if (collection.HasCache)
{ {
using var color = PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value()); using var color = PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value());
using var node = TreeNode($"{collection.Name} (Change Counter {collection.Counters.Change})###{collection.Name}"); using var node = TreeNode($"{collection.Identity.Name} (Change Counter {collection.Counters.Change})###{collection.Identity.Name}");
if (!node) if (!node)
continue; continue;
@ -239,7 +239,7 @@ public class DebugTab : Window, ITab, IUiService
else else
{ {
using var color = PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value()); using var color = PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value());
TreeNode($"{collection.AnonymizedName} (Change Counter {collection.Counters.Change})", TreeNode($"{collection.Identity.AnonymizedName} (Change Counter {collection.Counters.Change})",
ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose(); ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
} }
} }
@ -265,9 +265,9 @@ public class DebugTab : Window, ITab, IUiService
{ {
PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}"); PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}");
PrintValue("Git Commit Hash", _validityChecker.CommitHash); PrintValue("Git Commit Hash", _validityChecker.CommitHash);
PrintValue(TutorialService.SelectedCollection, _collectionManager.Active.Current.Name); PrintValue(TutorialService.SelectedCollection, _collectionManager.Active.Current.Identity.Name);
PrintValue(" has Cache", _collectionManager.Active.Current.HasCache.ToString()); PrintValue(" has Cache", _collectionManager.Active.Current.HasCache.ToString());
PrintValue(TutorialService.DefaultCollection, _collectionManager.Active.Default.Name); PrintValue(TutorialService.DefaultCollection, _collectionManager.Active.Default.Identity.Name);
PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString()); PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString());
PrintValue("Mod Manager BasePath", _modManager.BasePath.Name); PrintValue("Mod Manager BasePath", _modManager.BasePath.Name);
PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName); PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName);
@ -518,7 +518,7 @@ public class DebugTab : Window, ITab, IUiService
return; return;
ImGui.TextUnformatted( ImGui.TextUnformatted(
$"Last Game Object: 0x{_collectionResolver.IdentifyLastGameObjectCollection(true).AssociatedGameObject:X} ({_collectionResolver.IdentifyLastGameObjectCollection(true).ModCollection.Name})"); $"Last Game Object: 0x{_collectionResolver.IdentifyLastGameObjectCollection(true).AssociatedGameObject:X} ({_collectionResolver.IdentifyLastGameObjectCollection(true).ModCollection.Identity.Name})");
using (var drawTree = TreeNode("Draw Object to Object")) using (var drawTree = TreeNode("Draw Object to Object"))
{ {
if (drawTree) if (drawTree)
@ -545,7 +545,7 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TextUnformatted(name); ImGui.TextUnformatted(name);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var collection = _collectionResolver.IdentifyCollection(gameObject, true); var collection = _collectionResolver.IdentifyCollection(gameObject, true);
ImGui.TextUnformatted(collection.ModCollection.Name); ImGui.TextUnformatted(collection.ModCollection.Identity.Name);
} }
} }
} }
@ -561,7 +561,7 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TextUnformatted($"{data.AssociatedGameObject:X}"); ImGui.TextUnformatted($"{data.AssociatedGameObject:X}");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TextUnformatted(data.ModCollection.Name); ImGui.TextUnformatted(data.ModCollection.Identity.Name);
} }
} }
} }
@ -574,12 +574,12 @@ public class DebugTab : Window, ITab, IUiService
if (table) if (table)
{ {
ImGuiUtil.DrawTableColumn("Current Mtrl Data"); ImGuiUtil.DrawTableColumn("Current Mtrl Data");
ImGuiUtil.DrawTableColumn(_subfileHelper.MtrlData.ModCollection.Name); ImGuiUtil.DrawTableColumn(_subfileHelper.MtrlData.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}"); ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.MtrlData.AssociatedGameObject:X}");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.DrawTableColumn("Current Avfx Data"); ImGuiUtil.DrawTableColumn("Current Avfx Data");
ImGuiUtil.DrawTableColumn(_subfileHelper.AvfxData.ModCollection.Name); ImGuiUtil.DrawTableColumn(_subfileHelper.AvfxData.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}"); ImGuiUtil.DrawTableColumn($"0x{_subfileHelper.AvfxData.AssociatedGameObject:X}");
ImGui.TableNextColumn(); ImGui.TableNextColumn();
@ -591,7 +591,7 @@ public class DebugTab : Window, ITab, IUiService
foreach (var (resource, resolve) in _subfileHelper) foreach (var (resource, resolve) in _subfileHelper)
{ {
ImGuiUtil.DrawTableColumn($"0x{resource:X}"); ImGuiUtil.DrawTableColumn($"0x{resource:X}");
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Name); ImGuiUtil.DrawTableColumn(resolve.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}"); ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}");
ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}"); ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}");
} }
@ -611,7 +611,7 @@ public class DebugTab : Window, ITab, IUiService
ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}"); ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}");
ImGuiUtil.DrawTableColumn($"0x{address:X}"); ImGuiUtil.DrawTableColumn($"0x{address:X}");
ImGuiUtil.DrawTableColumn(identifier.ToString()); ImGuiUtil.DrawTableColumn(identifier.ToString());
ImGuiUtil.DrawTableColumn(collection.Name); ImGuiUtil.DrawTableColumn(collection.Identity.Name);
} }
} }
} }

View file

@ -77,12 +77,12 @@ public class ModsTab(
{ {
Penumbra.Log.Error($"Exception thrown during ModPanel Render:\n{e}"); Penumbra.Log.Error($"Exception thrown during ModPanel Render:\n{e}");
Penumbra.Log.Error($"{modManager.Count} Mods\n" Penumbra.Log.Error($"{modManager.Count} Mods\n"
+ $"{_activeCollections.Current.AnonymizedName} Current Collection\n" + $"{_activeCollections.Current.Identity.AnonymizedName} Current Collection\n"
+ $"{_activeCollections.Current.Settings.Count} Settings\n" + $"{_activeCollections.Current.Settings.Count} Settings\n"
+ $"{selector.SortMode.Name} Sort Mode\n" + $"{selector.SortMode.Name} Sort Mode\n"
+ $"{selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\n" + $"{selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\n"
+ $"{selector.Selected?.Name ?? "NULL"} Selected Mod\n" + $"{selector.Selected?.Name ?? "NULL"} Selected Mod\n"
+ $"{string.Join(", ", _activeCollections.Current.DirectlyInheritsFrom.Select(c => c.AnonymizedName))} Inheritances\n"); + $"{string.Join(", ", _activeCollections.Current.DirectlyInheritsFrom.Select(c => c.Identity.AnonymizedName))} Inheritances\n");
} }
} }

View file

@ -83,14 +83,14 @@ public class TutorialService : IUiService
+ "Go here after setting up your root folder to continue the tutorial!") + "Go here after setting up your root folder to continue the tutorial!")
.Register("Initial Setup, Step 4: Managing Collections", .Register("Initial Setup, Step 4: Managing Collections",
"On the left, we have the collection selector. Here, we can create new collections - either empty ones or by duplicating existing ones - and delete any collections not needed anymore.\n" "On the left, we have the collection selector. Here, we can create new collections - either empty ones or by duplicating existing ones - and delete any collections not needed anymore.\n"
+ $"There will always be one collection called {ModCollection.DefaultCollectionName} that can not be deleted.") + $"There will always be one collection called {ModCollectionIdentity.DefaultCollectionName} that can not be deleted.")
.Register($"Initial Setup, Step 5: {SelectedCollection}", .Register($"Initial Setup, Step 5: {SelectedCollection}",
$"The {SelectedCollection} is the one we highlighted in the selector. It is the collection we are currently looking at and editing.\nAny changes we make in our mod settings later in the next tab will edit this collection.\n" $"The {SelectedCollection} is the one we highlighted in the selector. It is the collection we are currently looking at and editing.\nAny changes we make in our mod settings later in the next tab will edit this collection.\n"
+ $"We should already have the collection named {ModCollection.DefaultCollectionName} selected, and for our simple setup, we do not need to do anything here.\n\n") + $"We should already have the collection named {ModCollectionIdentity.DefaultCollectionName} selected, and for our simple setup, we do not need to do anything here.\n\n")
.Register("Initial Setup, Step 6: Simple Assignments", .Register("Initial Setup, Step 6: Simple Assignments",
"Aside from being a collection of settings, we can also assign collections to different functions. This is used to make different mods apply to different characters.\n" "Aside from being a collection of settings, we can also assign collections to different functions. This is used to make different mods apply to different characters.\n"
+ "The Simple Assignments panel shows you the possible assignments that are enough for most people along with descriptions.\n" + "The Simple Assignments panel shows you the possible assignments that are enough for most people along with descriptions.\n"
+ $"If you are just starting, you can see that the {ModCollection.DefaultCollectionName} is currently assigned to {CollectionType.Default.ToName()} and {CollectionType.Interface.ToName()}.\n" + $"If you are just starting, you can see that the {ModCollectionIdentity.DefaultCollectionName} is currently assigned to {CollectionType.Default.ToName()} and {CollectionType.Interface.ToName()}.\n"
+ "You can also assign 'Use No Mods' instead of a collection by clicking on the function buttons.") + "You can also assign 'Use No Mods' instead of a collection by clicking on the function buttons.")
.Register("Individual Assignments", .Register("Individual Assignments",
"In the Individual Assignments panel, you can manually create assignments for very specific characters or monsters, not just yourself or ones you can currently target.") "In the Individual Assignments panel, you can manually create assignments for very specific characters or monsters, not just yourself or ones you can currently target.")