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 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)
{
@ -17,14 +17,14 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
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)
list.Add((collection.Id, collection.Name));
list.Add((collection.Identity.Id, collection.Identity.Name));
else if (identifier.Length >= 8)
list.AddRange(collections.Storage.Where(c => c.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase))
.Select(c => (c.Id, c.Name)));
list.AddRange(collections.Storage.Where(c => c.Identity.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase))
.Select(c => (c.Identity.Id, c.Identity.Name)));
list.AddRange(collections.Storage
.Where(c => string.Equals(c.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Id, c.Name)))
.Select(c => (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.Identity.Id, c.Identity.Name)));
return list;
}
@ -54,7 +54,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
return null;
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)
@ -64,17 +64,17 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
{
var id = helpers.AssociatedIdentifier(gameObjectIdx);
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))
return (true, true, (collection.Id, collection.Name));
return (true, true, (collection.Identity.Id, collection.Identity.Name));
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)
=> 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,
bool allowCreateNew, bool allowDelete)
@ -83,7 +83,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
return (PenumbraApiEc.InvalidArgument, null);
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 (old == null)
@ -106,7 +106,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
collections.Active.CreateSpecialCollection((CollectionType)type);
}
else if (old.Value.Item1 == collection.Id)
else if (old.Value.Item1 == collection.Identity.Id)
{
return (PenumbraApiEc.NothingChanged, old);
}
@ -120,10 +120,10 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
{
var id = helpers.AssociatedIdentifier(gameObjectIdx);
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 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 (old == null)
@ -148,7 +148,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
var ids = collections.Active.Individuals.GetGroup(id);
collections.Active.CreateIndividualCollection(ids);
}
else if (old.Value.Item1 == collection.Id)
else if (old.Value.Item1 == collection.Identity.Id)
{
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)
{
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)
@ -93,5 +93,5 @@ public class GameStateApi : IPenumbraApiGameState, IApiService, IDisposable
}
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))
return (PenumbraApiEc.CollectionMissing, null);
var settings = collection.Id == Guid.Empty
var settings = collection.Identity.Id == Guid.Empty
? null
: ignoreInheritance
? collection.Settings[mod.Index]
@ -217,7 +217,7 @@ public class ModSettingsApi : IPenumbraApiModSettings, IApiService, IDisposable
var collection = _collectionResolver.PlayerCollection();
var (settings, parent) = collection[mod.Index];
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)
@ -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)
=> 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,
int moveIndex)

View file

@ -146,10 +146,10 @@ public class TemporaryIpcTester(
using (ImRaii.PushFont(UiBuilder.MonoFont))
{
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.MetaCache?.Count.ToString() ?? "0");
ImGuiUtil.DrawTableColumn(string.Join(", ",
@ -199,7 +199,7 @@ public class TemporaryIpcTester(
{
PrintList("All", tempMods.ModsForAllCollections);
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)
{
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);
_communicator.ModSettingChanged.Invoke(collection, ModSettingChange.TemporaryMod, null, Setting.False, 0, false);
}
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);
_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 string AnonymizedName
=> _collection.AnonymizedName;
=> _collection.Identity.AnonymizedName;
public IEnumerable<SingleArray<ModConflicts>> AllConflicts
=> 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>
public bool CreateCache(ModCollection collection)
{
if (collection.Index == ModCollection.Empty.Index)
if (collection.Identity.Index == ModCollection.Empty.Identity.Index)
return false;
if (collection._cache != null)
return false;
collection._cache = new CollectionCache(this, collection);
if (collection.Index > 0)
if (collection.Identity.Index > 0)
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;
}
@ -132,32 +132,32 @@ public class CollectionCacheManager : IDisposable, IService
/// Does not create caches.
/// </summary>
public void CalculateEffectiveFileList(ModCollection collection)
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identifier,
=> _framework.RegisterImportant(nameof(CalculateEffectiveFileList) + collection.Identity.Identifier,
() => CalculateEffectiveFileListInternal(collection));
private void CalculateEffectiveFileListInternal(ModCollection collection)
{
// Skip the empty collection.
if (collection.Index == 0)
if (collection.Identity.Index == 0)
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)
{
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)
{
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
{
FullRecalculation(collection);
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
{
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);
if (type is CollectionType.Default)
@ -258,12 +258,12 @@ public class CollectionCacheManager : IDisposable, IService
private void RemoveCache(ModCollection? collection)
{
if (collection != null
&& collection.Index > ModCollection.Empty.Index
&& collection.Index != _active.Default.Index
&& collection.Index != _active.Interface.Index
&& collection.Index != _active.Current.Index
&& _active.SpecialAssignments.All(c => c.Value.Index != collection.Index)
&& _active.Individuals.All(c => c.Collection.Index != collection.Index))
&& collection.Identity.Index > ModCollection.Empty.Identity.Index
&& collection.Identity.Index != _active.Default.Identity.Index
&& collection.Identity.Index != _active.Interface.Identity.Index
&& collection.Identity.Index != _active.Current.Identity.Index
&& _active.SpecialAssignments.All(c => c.Value.Identity.Index != collection.Identity.Index)
&& _active.Individuals.All(c => c.Collection.Identity.Index != collection.Identity.Index))
ClearCache(collection);
}
@ -359,9 +359,9 @@ public class CollectionCacheManager : IDisposable, IService
collection._cache!.Dispose();
collection._cache = null;
if (collection.Index > 0)
if (collection.Identity.Index > 0)
Interlocked.Decrement(ref _count);
Penumbra.Log.Verbose($"Cleared cache of collection {collection.AnonymizedName}.");
Penumbra.Log.Verbose($"Cleared cache of collection {collection.Identity.AnonymizedName}.");
}
/// <summary>

View file

@ -59,7 +59,7 @@ public sealed class CollectionAutoSelector : IService, IDisposable
return;
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);
}

View file

@ -48,7 +48,7 @@ public static class ActiveCollectionMigration
if (!storage.ByName(collectionName, out var collection))
{
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);
}
else

View file

@ -219,7 +219,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
_ => null,
};
if (oldCollection == null || collection == oldCollection || collection.Index >= _storage.Count)
if (oldCollection == null || collection == oldCollection || collection.Identity.Index >= _storage.Count)
return;
switch (collectionType)
@ -262,13 +262,13 @@ public class ActiveCollections : ISavable, IDisposable, IService
var jObj = new JObject
{
{ nameof(Version), Version },
{ nameof(Default), Default.Id },
{ nameof(Interface), Interface.Id },
{ nameof(Current), Current.Id },
{ nameof(Default), Default.Identity.Id },
{ nameof(Interface), Interface.Identity.Id },
{ nameof(Current), Current.Identity.Id },
};
foreach (var (type, collection) in SpecialCollections.WithIndex().Where(p => p.Value != null)
.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());
using var j = new JsonTextWriter(writer);
@ -300,7 +300,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (oldCollection == Interface)
SetCollection(ModCollection.Empty, CollectionType.Interface);
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)
{
@ -325,11 +325,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
{
var configChanged = false;
// 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))
{
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);
Default = ModCollection.Empty;
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.
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))
{
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);
Interface = ModCollection.Empty;
configChanged = true;
@ -355,11 +355,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
}
// 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))
{
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);
Current = _storage.DefaultNamed;
configChanged = true;
@ -404,7 +404,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (!_storage.ById(defaultId, out var defaultCollection))
{
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);
Default = ModCollection.Empty;
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.
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))
{
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);
Interface = ModCollection.Empty;
configChanged = true;
@ -430,11 +430,11 @@ public class ActiveCollections : ISavable, IDisposable, IService
}
// 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))
{
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);
Current = _storage.DefaultNamed;
configChanged = true;
@ -587,7 +587,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
case IdentifierType.Player when id.HomeWorld != 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."
: string.Empty;
}
@ -596,12 +596,12 @@ public class ActiveCollections : ISavable, IDisposable, IService
{
var global = ByType(CollectionType.Individual,
_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.";
}
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."
: string.Empty;
}
@ -617,7 +617,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (maleNpc == null)
{
maleNpc = Default;
if (maleNpc.Index != checkAssignment.Index)
if (maleNpc.Identity.Index != checkAssignment.Identity.Index)
return string.Empty;
collection1 = CollectionType.Default;
@ -626,7 +626,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (femaleNpc == null)
{
femaleNpc = Default;
if (femaleNpc.Index != checkAssignment.Index)
if (femaleNpc.Identity.Index != checkAssignment.Identity.Index)
return string.Empty;
collection2 = CollectionType.Default;
@ -646,7 +646,7 @@ public class ActiveCollections : ISavable, IDisposable, IService
if (assignment == null)
continue;
if (assignment.Index == checkAssignment.Index)
if (assignment.Identity.Index == checkAssignment.Identity.Index)
return
$"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,
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);
_collectionsByLocal[CurrentCollectionId] = newCollection;
CurrentCollectionId += 1;
@ -57,7 +57,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
}
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>
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)
{
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;
return true;
@ -102,7 +102,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
{
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;
return true;
@ -158,7 +158,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
var newCollection = Create(name, _collections.Count, duplicate);
_collections.Add(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);
return true;
}
@ -168,13 +168,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
/// </summary>
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);
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);
return false;
@ -182,13 +182,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
Delete(collection);
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
_collections.RemoveAt(collection.Index);
_collections.RemoveAt(collection.Identity.Index);
// Update indices.
for (var i = collection.Index; i < Count; ++i)
_collections[i].Index = i;
_collectionsByLocal.Remove(collection.LocalId);
for (var i = collection.Identity.Index; i < Count; ++i)
_collections[i].Identity.Index = i;
_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);
return true;
}
@ -246,13 +246,13 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
{
File.Move(file.FullName, correctName, false);
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);
}
catch (Exception ex)
{
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);
}
}
@ -273,7 +273,7 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
catch (Exception 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);
}
@ -291,14 +291,14 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
/// </summary>
private ModCollection SetDefaultNamedCollection()
{
if (ByName(ModCollection.DefaultCollectionName, out var collection))
if (ByName(ModCollectionIdentity.DefaultCollectionName, out var collection))
return collection;
if (AddCollection(ModCollection.DefaultCollectionName, null))
if (AddCollection(ModCollectionIdentity.DefaultCollectionName, null))
return _collections[^1];
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);
return Count > 1 ? _collections[1] : _collections[0];
}

View file

@ -18,7 +18,7 @@ public partial class IndividualCollections
foreach (var (name, identifiers, collection) in Assignments)
{
var tmp = identifiers[0].ToJson();
tmp.Add("Collection", collection.Id);
tmp.Add("Collection", collection.Identity.Id);
tmp.Add("Display", name);
ret.Add(tmp);
}
@ -182,7 +182,7 @@ public partial class IndividualCollections
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
else
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);
}
// 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]}."));
// Try to migrate the player name without logging full names.
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
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);
}
else
{
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);
}
}

View file

@ -89,7 +89,7 @@ public class InheritanceManager : IDisposable, IService
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
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>
@ -101,7 +101,7 @@ public class InheritanceManager : IDisposable, IService
_saveService.QueueSave(new ModCollectionSave(_modStorage, inheritor));
_communicator.CollectionInheritanceChanged.Invoke(inheritor, false);
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)"/>
@ -119,7 +119,7 @@ public class InheritanceManager : IDisposable, IService
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;
}
@ -143,23 +143,23 @@ public class InheritanceManager : IDisposable, IService
continue;
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);
}
else if (_storage.ByName(subCollectionName, out subCollection))
{
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))
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);
}
else
{
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);
changes = true;
}

View file

@ -44,7 +44,7 @@ public class TempCollectionManager : IDisposable, IService
=> _customCollections.Values;
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)
=> _customCollections.TryGetValue(id, out collection);
@ -54,12 +54,12 @@ public class TempCollectionManager : IDisposable, IService
if (GlobalChangeCounter == int.MaxValue)
GlobalChangeCounter = 0;
var collection = _storage.CreateTemporary(name, ~Count, GlobalChangeCounter++);
Penumbra.Log.Debug($"Creating temporary collection {collection.Name} with {collection.Id}.");
if (_customCollections.TryAdd(collection.Id, collection))
Penumbra.Log.Debug($"Creating temporary collection {collection.Identity.Name} with {collection.Identity.Id}.");
if (_customCollections.TryAdd(collection.Identity.Id, collection))
{
// Temporary collection created.
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
return collection.Id;
return collection.Identity.Id;
}
return Guid.Empty;
@ -74,7 +74,7 @@ public class TempCollectionManager : IDisposable, IService
}
_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);
for (var i = 0; i < Collections.Count; ++i)
{
@ -83,7 +83,7 @@ public class TempCollectionManager : IDisposable, IService
// Temporary collection assignment removed.
_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--);
}
@ -96,7 +96,7 @@ public class TempCollectionManager : IDisposable, IService
return false;
// 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);
return true;
}
@ -127,6 +127,6 @@ public class TempCollectionManager : IDisposable, IService
return false;
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
/// - Settings has the same size as ModManager.Mods.
/// - 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>
public partial class ModCollection
{
public const int CurrentVersion = 2;
public const string DefaultCollectionName = "Default";
public const string EmptyCollectionName = "None";
public const int CurrentVersion = 2;
/// <summary>
/// Create the always available Empty Collection that will always sit at index 0,
/// can not be deleted and does never create a cache.
/// </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 string Name { get; set; }
public Guid Id { get; }
public LocalCollectionId LocalId { get; }
public string Identifier
=> Id.ToString();
public string ShortIdentifier
=> Identifier[..8];
public ModCollectionIdentity Identity;
public override string ToString()
=> Name.Length > 0 ? Name : ShortIdentifier;
/// <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; }
=> Identity.ToString();
public CollectionCounters Counters;
@ -90,7 +69,7 @@ public partial class ModCollection
{
get
{
if (Index <= 0)
if (Identity.Index <= 0)
return (ModSettings.Empty, this);
foreach (var collection in GetFlattenedInheritance())
@ -114,17 +93,17 @@ public partial class ModCollection
public ModCollection Duplicate(string name, LocalCollectionId localId, int 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(),
[.. DirectlyInheritsFrom], UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()));
return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
Settings.Select(s => s?.DeepCopy()).ToList(), [.. DirectlyInheritsFrom],
UnusedSettings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.DeepCopy()));
}
/// <summary> Constructor for reading from files. </summary>
public static ModCollection CreateFromData(SaveService saver, ModStorage mods, Guid id, string name, LocalCollectionId localId, int version,
int index,
public static ModCollection CreateFromData(SaveService saver, ModStorage mods, ModCollectionIdentity identity, int version,
Dictionary<string, ModSettings.SavedSettings> allSettings, IReadOnlyList<string> inheritances)
{
Debug.Assert(index > 0, "Collection read with non-positive index.");
var ret = new ModCollection(id, name, localId, index, 0, version, [], [], allSettings)
Debug.Assert(identity.Index > 0, "Collection read with non-positive index.");
var ret = new ModCollection(identity, 0, version, [], [], allSettings)
{
InheritanceByName = inheritances,
};
@ -137,7 +116,7 @@ public partial class ModCollection
public static ModCollection CreateTemporary(string name, LocalCollectionId localId, int index, int changeCounter)
{
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;
}
@ -145,9 +124,8 @@ public partial class ModCollection
public static ModCollection CreateEmpty(string name, LocalCollectionId localId, int index, int modCount)
{
Debug.Assert(index >= 0, "Empty collection created with negative index.");
return new ModCollection(Guid.NewGuid(), name, localId, index, 0, CurrentVersion,
Enumerable.Repeat((ModSettings?)null, modCount).ToList(), [],
[]);
return new ModCollection(ModCollectionIdentity.New(name, localId, index), 0, CurrentVersion,
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>
@ -195,16 +173,13 @@ public partial class ModCollection
saver.ImmediateSave(new ModCollectionSave(mods, this));
}
private ModCollection(Guid id, string name, LocalCollectionId localId, int index, int changeCounter, int version,
List<ModSettings?> appliedSettings, List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings)
private ModCollection(ModCollectionIdentity identity, int changeCounter, int version, List<ModSettings?> appliedSettings,
List<ModCollection> inheritsFrom, Dictionary<string, ModSettings.SavedSettings> settings)
{
Name = name;
Id = id;
LocalId = localId;
Index = index;
Identity = identity;
Counters = new CollectionCounters(changeCounter);
Settings = appliedSettings;
UnusedSettings = settings;
Settings = appliedSettings;
UnusedSettings = settings;
DirectlyInheritsFrom = inheritsFrom;
foreach (var c in DirectlyInheritsFrom)
((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);
public string LogName(string _)
=> modCollection.AnonymizedName;
=> modCollection.Identity.AnonymizedName;
public string TypeName
=> "Collection";
@ -28,10 +28,10 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
j.WriteStartObject();
j.WritePropertyName("Version");
j.WriteValue(ModCollection.CurrentVersion);
j.WritePropertyName(nameof(ModCollection.Id));
j.WriteValue(modCollection.Identifier);
j.WritePropertyName(nameof(ModCollection.Name));
j.WriteValue(modCollection.Name);
j.WritePropertyName(nameof(ModCollectionIdentity.Id));
j.WriteValue(modCollection.Identity.Identifier);
j.WritePropertyName(nameof(ModCollectionIdentity.Name));
j.WriteValue(modCollection.Identity.Name);
j.WritePropertyName(nameof(ModCollection.Settings));
// 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.
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();
}
@ -79,8 +79,8 @@ internal readonly struct ModCollectionSave(ModStorage modStorage, ModCollection
{
var obj = JObject.Parse(File.ReadAllText(file.FullName));
version = obj["Version"]?.ToObject<int>() ?? 0;
name = obj[nameof(ModCollection.Name)]?.ToObject<string>() ?? string.Empty;
id = obj[nameof(ModCollection.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty);
name = obj[nameof(ModCollectionIdentity.Name)]?.ToObject<string>() ?? string.Empty;
id = obj[nameof(ModCollectionIdentity.Id)]?.ToObject<Guid>() ?? (version == 1 ? Guid.NewGuid() : Guid.Empty);
// Custom deserialization that is converted with the constructor.
settings = obj[nameof(ModCollection.Settings)]?.ToObject<Dictionary<string, ModSettings.SavedSettings>>() ?? settings;
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()
=> ModCollection.Name;
=> ModCollection.Identity.Name;
}
public static class ResolveDataExtensions

View file

@ -326,7 +326,7 @@ public class CommandHandler : IDisposable, IApiService
{
_chat.Print(collection == null
? $"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;
}
@ -363,13 +363,13 @@ public class CommandHandler : IDisposable, IApiService
}
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;
continue;
}
_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;
@ -440,7 +440,7 @@ public class CommandHandler : IDisposable, IApiService
_chat.Print(new SeStringBuilder().AddText("Mod ").AddPurple(mod.Name, true)
.AddText("already had the desired state in collection ")
.AddYellow(collection!.Name, true).AddText(".").BuiltString);
.AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
return false;
}
@ -458,7 +458,7 @@ public class CommandHandler : IDisposable, IApiService
_collectionEditor.SetModSetting(collection!, mod, groupIndex, setting);
Print(() => new SeStringBuilder().AddText("Changed settings of group ").AddGreen(groupName, true).AddText(" in mod ")
.AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection!.Name, true).AddText(".").BuiltString);
.AddYellow(collection!.Identity.Name, true).AddText(".").BuiltString);
return true;
}
@ -543,7 +543,7 @@ public class CommandHandler : IDisposable, IApiService
changes |= HandleModState(state, collection!, mod);
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);
return true;
@ -558,7 +558,7 @@ public class CommandHandler : IDisposable, IApiService
return true;
}
collection = string.Equals(lowerName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase)
collection = string.Equals(lowerName, ModCollection.Empty.Identity.Name, StringComparison.OrdinalIgnoreCase)
? ModCollection.Empty
: _collectionManager.Storage.ByIdentifier(lowerName, out var c)
? c
@ -614,7 +614,7 @@ public class CommandHandler : IDisposable, IApiService
return false;
Print(() => new SeStringBuilder().AddText("Enabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString);
return true;
@ -623,7 +623,7 @@ public class CommandHandler : IDisposable, IApiService
return false;
Print(() => new SeStringBuilder().AddText("Disabled mod ").AddPurple(mod.Name, true).AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString);
return true;
@ -634,7 +634,7 @@ public class CommandHandler : IDisposable, IApiService
Print(() => new SeStringBuilder().AddText(setting ? "Enabled mod " : "Disabled mod ").AddPurple(mod.Name, true)
.AddText(" in collection ")
.AddYellow(collection.Name, true)
.AddYellow(collection.Identity.Name, true)
.AddText(".").BuiltString);
return true;
@ -643,7 +643,7 @@ public class CommandHandler : IDisposable, IApiService
return false;
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);
return true;
}

View file

@ -30,7 +30,7 @@ public unsafe class AtchCallerHook1 : FastHook<AtchCallerHook1.Delegate>, IDispo
Task.Result.Original(data, slot, unk, playerModel);
_metaState.AtchCollection.Pop();
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()

View file

@ -30,7 +30,7 @@ public unsafe class AtchCallerHook2 : FastHook<AtchCallerHook2.Delegate>, IDispo
Task.Result.Original(data, slot, unk, playerModel, unk2);
_metaState.AtchCollection.Pop();
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()

View file

@ -98,7 +98,7 @@ public sealed unsafe class MetaState : IDisposable, IService
_lastCreatedCollection = _collectionResolver.IdentifyLastGameObjectCollection(true);
if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero)
_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,
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -47,17 +47,17 @@ public static class PathDataHandler
/// <summary> Create the encoding path for an ATCH file. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View file

@ -26,6 +26,6 @@ public sealed class ImcFilePostProcessor(CollectionStorage collections) : IFileP
file.Replace(resource);
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 networked = character.EntityId != 0xE0000000;
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,
cache, (flags & Flags.WithUiData) != 0);
using (var _ = pathState.EnterInternalResolve())

View file

@ -63,10 +63,10 @@ public class TemporaryMod : IMod
DirectoryInfo? dir = null;
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"));
modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Id} for {character ?? "Unknown Character"} with name {collection.Name}.",
modManager.DataEditor.CreateMeta(dir, collection.Identity.Name, character ?? config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Identity.Id} for {character ?? "Unknown Character"} with name {collection.Identity.Name}.",
null, null);
var mod = new Mod(dir);
var defaultMod = mod.Default;
@ -99,11 +99,11 @@ public class TemporaryMod : IMod
saveService.ImmediateSaveSync(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
modManager.AddMod(dir, false);
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)
{
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))
{
try

View file

@ -245,24 +245,24 @@ public class Penumbra : IDalamudPlugin
void PrintCollection(ModCollection c, CollectionCache _)
=> 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.Append($"> **`#Collections: `** {_collectionManager.Storage.Count - 1}\n");
sb.Append($"> **`#Temp Collections: `** {_tempCollections.Count}\n");
sb.Append($"> **`Active Collections: `** {_collectionManager.Caches.Count}\n");
sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.AnonymizedName}\n");
sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.AnonymizedName}\n");
sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.AnonymizedName}\n");
sb.Append($"> **`Base Collection: `** {_collectionManager.Active.Default.Identity.AnonymizedName}\n");
sb.Append($"> **`Interface Collection: `** {_collectionManager.Active.Interface.Identity.AnonymizedName}\n");
sb.Append($"> **`Selected Collection: `** {_collectionManager.Active.Current.Identity.AnonymizedName}\n");
foreach (var (type, name, _) in CollectionTypeExtensions.Special)
{
var collection = _collectionManager.Active.ByType(type);
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)
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)
PrintCollection(collection, collection._cache!);

View file

@ -27,8 +27,8 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
private Configuration _config = null!;
private JObject _data = null!;
public string CurrentCollection = ModCollection.DefaultCollectionName;
public string DefaultCollection = ModCollection.DefaultCollectionName;
public string CurrentCollection = ModCollectionIdentity.DefaultCollectionName;
public string DefaultCollection = ModCollectionIdentity.DefaultCollectionName;
public string ForcedCollection = string.Empty;
public Dictionary<string, string> CharacterCollections = [];
public Dictionary<string, string> ModSortOrder = [];
@ -346,7 +346,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
if (!collectionJson.Exists)
return;
var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollection.DefaultCollectionName));
var defaultCollectionFile = new FileInfo(saveService.FileNames.CollectionFile(ModCollectionIdentity.DefaultCollectionName));
if (defaultCollectionFile.Exists)
return;
@ -380,7 +380,7 @@ public class ConfigMigrationService(SaveService saveService, BackupService backu
var emptyStorage = new ModStorage();
// 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));
}
catch (Exception e)

View file

@ -240,7 +240,7 @@ public sealed class CrashHandlerService : IDisposable, IService
var name = GetActorName(character);
lock (_eventWriter)
{
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Id, type);
_eventWriter?.AnimationFuncInvoked.WriteLine(character, name.Span, collection.Identity.Id, type);
}
}
catch (Exception ex)
@ -293,7 +293,7 @@ public sealed class CrashHandlerService : IDisposable, IService
var name = GetActorName(resolveData.AssociatedGameObject);
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);
}
}

View file

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

View file

@ -25,7 +25,7 @@ public class CollectionSelectHeader : IUiService
_selection = selection;
_resolver = resolver;
_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>
@ -77,10 +77,10 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch
{
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),
CollectionState.Available => (collection, collection.Name,
$"Select the configured base collection {collection.Name} as the current collection.", false),
CollectionState.Available => (collection, collection.Identity.Name,
$"Select the configured base collection {collection.Identity.Name} as the current collection.", false),
_ => throw new Exception("Can not happen."),
};
}
@ -91,10 +91,11 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch
{
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),
CollectionState.Available => (collection, collection.Name,
$"Select the collection {collection.Name} that applies to the loaded player character as the current collection.", false),
CollectionState.Available => (collection, collection.Identity.Name,
$"Select the collection {collection.Identity.Name} that applies to the loaded player character as the current collection.",
false),
_ => throw new Exception("Can not happen."),
};
}
@ -105,10 +106,10 @@ public class CollectionSelectHeader : IUiService
return CheckCollection(collection) switch
{
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),
CollectionState.Available => (collection, collection.Name,
$"Select the configured interface collection {collection.Name} as the current collection.", false),
CollectionState.Available => (collection, collection.Identity.Name,
$"Select the configured interface collection {collection.Identity.Name} as the current collection.", false),
_ => throw new Exception("Can not happen."),
};
}
@ -120,8 +121,8 @@ public class CollectionSelectHeader : IUiService
{
CollectionState.Unavailable => (null, "Not Inherited",
"The settings of the selected mod are not inherited from another collection.", true),
CollectionState.Available => (collection, collection!.Name,
$"Select the collection {collection!.Name} from which the selected mod inherits its settings as the current collection.",
CollectionState.Available => (collection, collection!.Identity.Name,
$"Select the collection {collection!.Identity.Name} from which the selected mod inherits its settings as the current collection.",
false),
_ => 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);
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);
_color.Dispose();
}
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,
ImGuiComboFlags flags)

View file

@ -221,16 +221,16 @@ public sealed class CollectionPanel(
ImGui.SameLine();
ImGui.BeginGroup();
using var style = ImRaii.PushStyle(ImGuiStyleVar.ButtonTextAlign, new Vector2(0, 0.5f));
var name = _newName ?? collection.Name;
var identifier = collection.Identifier;
var name = _newName ?? collection.Identity.Name;
var identifier = collection.Identity.Identifier;
var width = ImGui.GetContentRegionAvail().X;
var fileName = saveService.FileNames.CollectionFile(collection);
ImGui.SetNextItemWidth(width);
if (ImGui.InputText("##name", ref name, 128))
_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));
selector.RestoreCollections();
_newName = null;
@ -242,7 +242,7 @@ public sealed class CollectionPanel(
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
{
Process.Start(new ProcessStartInfo(fileName) { UseShellExecute = true });
@ -289,9 +289,9 @@ public sealed class CollectionPanel(
_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));
}
}
@ -418,7 +418,7 @@ public sealed class CollectionPanel(
private string Name(ModCollection? collection)
=> collection == null ? "Unassigned" :
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)
{

View file

@ -69,7 +69,7 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
}
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";
@ -111,12 +111,12 @@ public sealed class CollectionSelector : ItemSelector<ModCollection>, IDisposabl
}
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()
{
Items.Clear();
foreach (var c in _storage.OrderBy(c => c.Name))
foreach (var c in _storage.OrderBy(c => c.Identity.Name))
Items.Add(c);
SetFilterDirty();
SetCurrent(_active.Current);

View file

@ -93,7 +93,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
/// </summary>
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();
// Get start point for the lines (top of the selector).
@ -114,7 +114,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
_seenInheritedCollections.Contains(inheritance));
_seenInheritedCollections.Add(inheritance);
ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Id}",
ImRaii.TreeNode($"{Name(inheritance)}###{inheritance.Identity.Id}",
ImGuiTreeNodeFlags.NoTreePushOnOpen | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.Bullet);
var (minRect, maxRect) = (ImGui.GetItemRectMin(), ImGui.GetItemRectMax());
DrawInheritanceTreeClicks(inheritance, false);
@ -140,7 +140,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.HandledConflictMod.Value(),
_seenInheritedCollections.Contains(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();
DrawInheritanceTreeClicks(collection, true);
DrawInheritanceDropSource(collection);
@ -252,7 +252,7 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
foreach (var collection in _collections
.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))
_newInheritance = collection;
@ -312,5 +312,5 @@ public class InheritanceUi(CollectionManager collectionManager, IncognitoService
}
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())
{
using var id = ImUtf8.PushId(idx);
ImUtf8.DrawTableColumn(collection.Name);
ImUtf8.DrawTableColumn(collection.Identity.Name);
ImGui.TableNextColumn();
ImUtf8.Text(ToText(state), color);
@ -65,7 +65,7 @@ public class ModPanelCollectionsTab(CollectionManager manager, ModFileSystemSele
{
if (context)
{
ImUtf8.Text(collection.Name);
ImUtf8.Text(collection.Identity.Name);
ImGui.Separator();
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);
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);
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;
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;
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>

View file

@ -204,7 +204,7 @@ public class DebugTab : Window, ITab, IUiService
if (collection.HasCache)
{
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)
continue;
@ -239,7 +239,7 @@ public class DebugTab : Window, ITab, IUiService
else
{
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();
}
}
@ -265,9 +265,9 @@ public class DebugTab : Window, ITab, IUiService
{
PrintValue("Penumbra Version", $"{_validityChecker.Version} {DebugVersionString}");
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(TutorialService.DefaultCollection, _collectionManager.Active.Default.Name);
PrintValue(TutorialService.DefaultCollection, _collectionManager.Active.Default.Identity.Name);
PrintValue(" has Cache", _collectionManager.Active.Default.HasCache.ToString());
PrintValue("Mod Manager BasePath", _modManager.BasePath.Name);
PrintValue("Mod Manager BasePath-Full", _modManager.BasePath.FullName);
@ -518,7 +518,7 @@ public class DebugTab : Window, ITab, IUiService
return;
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"))
{
if (drawTree)
@ -545,7 +545,7 @@ public class DebugTab : Window, ITab, IUiService
ImGui.TextUnformatted(name);
ImGui.TableNextColumn();
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.TextUnformatted($"{data.AssociatedGameObject:X}");
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)
{
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}");
ImGui.TableNextColumn();
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}");
ImGui.TableNextColumn();
@ -591,7 +591,7 @@ public class DebugTab : Window, ITab, IUiService
foreach (var (resource, resolve) in _subfileHelper)
{
ImGuiUtil.DrawTableColumn($"0x{resource:X}");
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Name);
ImGuiUtil.DrawTableColumn(resolve.ModCollection.Identity.Name);
ImGuiUtil.DrawTableColumn($"0x{resolve.AssociatedGameObject:X}");
ImGuiUtil.DrawTableColumn($"{((ResourceHandle*)resource)->FileName()}");
}
@ -611,7 +611,7 @@ public class DebugTab : Window, ITab, IUiService
ImGuiUtil.DrawTableColumn($"{((GameObject*)address)->ObjectIndex}");
ImGuiUtil.DrawTableColumn($"0x{address:X}");
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($"{modManager.Count} Mods\n"
+ $"{_activeCollections.Current.AnonymizedName} Current Collection\n"
+ $"{_activeCollections.Current.Identity.AnonymizedName} Current Collection\n"
+ $"{_activeCollections.Current.Settings.Count} Settings\n"
+ $"{selector.SortMode.Name} Sort Mode\n"
+ $"{selector.SelectedLeaf?.Name ?? "NULL"} Selected Leaf\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!")
.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"
+ $"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}",
$"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",
"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"
+ $"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.")
.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.")