mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 14:23:43 +01:00
Rework API, use Collection ID in crash handler, use collection GUIDs in more places.
This commit is contained in:
parent
793ed4f0a7
commit
ba8999914f
88 changed files with 4193 additions and 3930 deletions
|
|
@ -22,7 +22,7 @@ public class ActiveCollectionData
|
|||
|
||||
public class ActiveCollections : ISavable, IDisposable
|
||||
{
|
||||
public const int Version = 1;
|
||||
public const int Version = 2;
|
||||
|
||||
private readonly CollectionStorage _storage;
|
||||
private readonly CommunicatorService _communicator;
|
||||
|
|
@ -261,16 +261,17 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
var jObj = new JObject
|
||||
{
|
||||
{ nameof(Version), Version },
|
||||
{ nameof(Default), Default.Name },
|
||||
{ nameof(Interface), Interface.Name },
|
||||
{ nameof(Current), Current.Name },
|
||||
{ nameof(Default), Default.Id },
|
||||
{ nameof(Interface), Interface.Id },
|
||||
{ nameof(Current), Current.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.Name);
|
||||
jObj.Add(type.ToString(), collection.Id);
|
||||
|
||||
jObj.Add(nameof(Individuals), Individuals.ToJObject());
|
||||
using var j = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
|
||||
using var j = new JsonTextWriter(writer);
|
||||
j.Formatting = Formatting.Indented;
|
||||
jObj.WriteTo(j);
|
||||
}
|
||||
|
||||
|
|
@ -319,22 +320,16 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load default, current, special, and character collections from config.
|
||||
/// If a collection does not exist anymore, reset it to an appropriate default.
|
||||
/// </summary>
|
||||
private void LoadCollections()
|
||||
private bool LoadCollectionsV1(JObject jObject)
|
||||
{
|
||||
Penumbra.Log.Debug("[Collections] Reading collection assignments...");
|
||||
var configChanged = !Load(_saveService.FileNames, out var jObject);
|
||||
|
||||
// Load the default collection. If the string does not exist take the Default name if no file existed or the Empty name if one existed.
|
||||
var defaultName = jObject[nameof(Default)]?.ToObject<string>()
|
||||
?? (configChanged ? ModCollection.DefaultCollectionName : ModCollection.Empty.Name);
|
||||
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;
|
||||
if (!_storage.ByName(defaultName, out var defaultCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
|
||||
$"Last choice of {TutorialService.DefaultCollection} {defaultName} is not available, reset to {ModCollection.Empty.Name}.",
|
||||
NotificationType.Warning);
|
||||
Default = ModCollection.Empty;
|
||||
configChanged = true;
|
||||
}
|
||||
|
|
@ -348,7 +343,8 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
if (!_storage.ByName(interfaceName, out var interfaceCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.", NotificationType.Warning);
|
||||
$"Last choice of {TutorialService.InterfaceCollection} {interfaceName} is not available, reset to {ModCollection.Empty.Name}.",
|
||||
NotificationType.Warning);
|
||||
Interface = ModCollection.Empty;
|
||||
configChanged = true;
|
||||
}
|
||||
|
|
@ -362,7 +358,8 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
if (!_storage.ByName(currentName, out var currentCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.", NotificationType.Warning);
|
||||
$"Last choice of {TutorialService.SelectedCollection} {currentName} is not available, reset to {ModCollection.DefaultCollectionName}.",
|
||||
NotificationType.Warning);
|
||||
Current = _storage.DefaultNamed;
|
||||
configChanged = true;
|
||||
}
|
||||
|
|
@ -393,11 +390,124 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
Penumbra.Log.Debug("[Collections] Loaded non-individual collection assignments.");
|
||||
|
||||
configChanged |= ActiveCollectionMigration.MigrateIndividualCollections(_storage, Individuals, jObject);
|
||||
configChanged |= Individuals.ReadJObject(_saveService, this, jObject[nameof(Individuals)] as JArray, _storage);
|
||||
configChanged |= Individuals.ReadJObject(_saveService, this, jObject[nameof(Individuals)] as JArray, _storage, 1);
|
||||
|
||||
// Save any changes.
|
||||
if (configChanged)
|
||||
_saveService.ImmediateSave(this);
|
||||
return configChanged;
|
||||
}
|
||||
|
||||
private bool LoadCollectionsV2(JObject jObject)
|
||||
{
|
||||
var configChanged = false;
|
||||
// Load the default collection. If the guid does not exist take the empty collection.
|
||||
var defaultId = jObject[nameof(Default)]?.ToObject<Guid>() ?? Guid.Empty;
|
||||
if (!_storage.ById(defaultId, out var defaultCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.DefaultCollection} {defaultId} is not available, reset to {ModCollection.Empty.Name}.",
|
||||
NotificationType.Warning);
|
||||
Default = ModCollection.Empty;
|
||||
configChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Default = defaultCollection;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (!_storage.ById(interfaceId, out var interfaceCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.InterfaceCollection} {interfaceId} is not available, reset to {ModCollection.Empty.Name}.",
|
||||
NotificationType.Warning);
|
||||
Interface = ModCollection.Empty;
|
||||
configChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Interface = interfaceCollection;
|
||||
}
|
||||
|
||||
// Load the current collection.
|
||||
var currentId = jObject[nameof(Current)]?.ToObject<Guid>() ?? _storage.DefaultNamed.Id;
|
||||
if (!_storage.ById(currentId, out var currentCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Last choice of {TutorialService.SelectedCollection} {currentId} is not available, reset to {ModCollection.DefaultCollectionName}.",
|
||||
NotificationType.Warning);
|
||||
Current = _storage.DefaultNamed;
|
||||
configChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = currentCollection;
|
||||
}
|
||||
|
||||
// Load special collections.
|
||||
foreach (var (type, name, _) in CollectionTypeExtensions.Special)
|
||||
{
|
||||
var typeId = jObject[type.ToString()]?.ToObject<Guid>();
|
||||
if (typeId == null)
|
||||
continue;
|
||||
|
||||
if (!_storage.ById(typeId.Value, out var typeCollection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage($"Last choice of {name} Collection {typeId.Value} is not available, removed.",
|
||||
NotificationType.Warning);
|
||||
configChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpecialCollections[(int)type] = typeCollection;
|
||||
}
|
||||
}
|
||||
|
||||
Penumbra.Log.Debug("[Collections] Loaded non-individual collection assignments.");
|
||||
|
||||
configChanged |= ActiveCollectionMigration.MigrateIndividualCollections(_storage, Individuals, jObject);
|
||||
configChanged |= Individuals.ReadJObject(_saveService, this, jObject[nameof(Individuals)] as JArray, _storage, 2);
|
||||
|
||||
return configChanged;
|
||||
}
|
||||
|
||||
private bool LoadCollectionsNew()
|
||||
{
|
||||
Current = _storage.DefaultNamed;
|
||||
Default = _storage.DefaultNamed;
|
||||
Interface = _storage.DefaultNamed;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load default, current, special, and character collections from config.
|
||||
/// If a collection does not exist anymore, reset it to an appropriate default.
|
||||
/// </summary>
|
||||
private void LoadCollections()
|
||||
{
|
||||
Penumbra.Log.Debug("[Collections] Reading collection assignments...");
|
||||
var configChanged = !Load(_saveService.FileNames, out var jObject);
|
||||
var version = jObject["Version"]?.ToObject<int>() ?? 0;
|
||||
var changed = false;
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
changed = LoadCollectionsV1(jObject);
|
||||
break;
|
||||
case 2:
|
||||
changed = LoadCollectionsV2(jObject);
|
||||
break;
|
||||
case 0 when configChanged:
|
||||
changed = LoadCollectionsNew();
|
||||
break;
|
||||
case 0:
|
||||
Penumbra.Messager.NotificationMessage("Active Collections File has unknown version and will be reset.",
|
||||
NotificationType.Warning);
|
||||
changed = LoadCollectionsNew();
|
||||
break;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
_saveService.ImmediateSaveSync(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -410,7 +520,7 @@ public class ActiveCollections : ISavable, IDisposable
|
|||
var jObj = BackupService.GetJObjectForFile(fileNames, file);
|
||||
if (jObj == null)
|
||||
{
|
||||
ret = new JObject();
|
||||
ret = [];
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Mods.Editor;
|
||||
|
|
@ -48,6 +47,25 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Find a collection by its id. If the GUID is empty, the empty collection is returned. </summary>
|
||||
public bool ById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
||||
{
|
||||
if (id != Guid.Empty)
|
||||
return _collections.FindFirst(c => c.Id == id, out collection);
|
||||
|
||||
collection = ModCollection.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Find a collection by an identifier, which is interpreted as a GUID first and if it does not correspond to one, as a name. </summary>
|
||||
public bool ByIdentifier(string identifier, [NotNullWhen(true)] out ModCollection? collection)
|
||||
{
|
||||
if (Guid.TryParse(identifier, out var guid))
|
||||
return ById(guid, out collection);
|
||||
|
||||
return ByName(identifier, out collection);
|
||||
}
|
||||
|
||||
public CollectionStorage(CommunicatorService communicator, SaveService saveService, ModStorage modStorage)
|
||||
{
|
||||
_communicator = communicator;
|
||||
|
|
@ -70,31 +88,6 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
_communicator.ModFileChanged.Unsubscribe(OnModFileChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the name is not empty, it is not the name of the empty collection
|
||||
/// and no existing collection results in the same filename as name. Also returns the fixed name.
|
||||
/// </summary>
|
||||
public bool CanAddCollection(string name, out string fixedName)
|
||||
{
|
||||
if (!IsValidName(name))
|
||||
{
|
||||
fixedName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
name = name.ToLowerInvariant();
|
||||
if (name.Length == 0
|
||||
|| name == ModCollection.Empty.Name.ToLowerInvariant()
|
||||
|| _collections.Any(c => c.Name.ToLowerInvariant() == name))
|
||||
{
|
||||
fixedName = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
fixedName = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new collection of the given name.
|
||||
/// If duplicate is not-null, the new collection will be a duplicate of it.
|
||||
|
|
@ -104,14 +97,6 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
/// </summary>
|
||||
public bool AddCollection(string name, ModCollection? duplicate)
|
||||
{
|
||||
if (!CanAddCollection(name, out var fixedName))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"The new collection {name} would lead to the same path {fixedName} as one that already exists.", NotificationType.Warning,
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
|
||||
var newCollection = duplicate?.Duplicate(name, _collections.Count)
|
||||
?? ModCollection.CreateEmpty(name, _collections.Count, _modStorage.Count);
|
||||
_collections.Add(newCollection);
|
||||
|
|
@ -166,16 +151,9 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a name is valid to use for a collection.
|
||||
/// Does not check for uniqueness.
|
||||
/// </summary>
|
||||
private static bool IsValidName(string name)
|
||||
=> name.Length is > 0 and < 64 && name.All(c => !c.IsInvalidAscii() && c is not '|' && !c.IsInvalidInPath());
|
||||
|
||||
/// <summary>
|
||||
/// Read all collection files in the Collection Directory.
|
||||
/// Ensure that the default named collection exists, and apply inheritances afterwards.
|
||||
/// Ensure that the default named collection exists, and apply inheritances afterward.
|
||||
/// Duplicate collection files are not deleted, just not added here.
|
||||
/// </summary>
|
||||
private void ReadCollections(out ModCollection defaultNamedCollection)
|
||||
|
|
@ -183,29 +161,46 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable
|
|||
Penumbra.Log.Debug("[Collections] Reading saved collections...");
|
||||
foreach (var file in _saveService.FileNames.CollectionFiles)
|
||||
{
|
||||
if (!ModCollectionSave.LoadFromFile(file, out var name, out var version, out var settings, out var inheritance))
|
||||
if (!ModCollectionSave.LoadFromFile(file, out var id, out var name, out var version, out var settings, out var inheritance))
|
||||
continue;
|
||||
|
||||
if (!IsValidName(name))
|
||||
if (id == Guid.Empty)
|
||||
{
|
||||
// TODO: handle better.
|
||||
Penumbra.Messager.NotificationMessage($"Collection of unsupported name found: {name} is not a valid collection name.",
|
||||
Penumbra.Messager.NotificationMessage("Collection without ID found.", NotificationType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ById(id, out _))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage($"Duplicate collection found: {id} already exists. Import skipped.",
|
||||
NotificationType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ByName(name, out _))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage($"Duplicate collection found: {name} already exists. Import skipped.",
|
||||
NotificationType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
var collection = ModCollection.CreateFromData(_saveService, _modStorage, name, version, Count, settings, inheritance);
|
||||
var collection = ModCollection.CreateFromData(_saveService, _modStorage, id, name, version, Count, settings, inheritance);
|
||||
var correctName = _saveService.FileNames.CollectionFile(collection);
|
||||
if (file.FullName != correctName)
|
||||
Penumbra.Messager.NotificationMessage($"Collection {file.Name} does not correspond to {collection.Name}.",
|
||||
NotificationType.Warning);
|
||||
try
|
||||
{
|
||||
if (version >= 2)
|
||||
{
|
||||
File.Move(file.FullName, correctName, false);
|
||||
Penumbra.Messager.NotificationMessage($"Collection {file.Name} does not correspond to {collection.Identifier}, renamed.",
|
||||
NotificationType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveService.ImmediateSaveSync(new ModCollectionSave(_modStorage, collection));
|
||||
File.Delete(file.FullName);
|
||||
Penumbra.Log.Information($"Migrated collection {name} to Guid {id}.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(e,
|
||||
$"Collection {file.Name} does not correspond to {collection.Identifier}, but could not rename.", NotificationType.Error);
|
||||
}
|
||||
|
||||
_collections.Add(collection);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public partial class IndividualCollections
|
|||
foreach (var (name, identifiers, collection) in Assignments)
|
||||
{
|
||||
var tmp = identifiers[0].ToJson();
|
||||
tmp.Add("Collection", collection.Name);
|
||||
tmp.Add("Collection", collection.Id);
|
||||
tmp.Add("Display", name);
|
||||
ret.Add(tmp);
|
||||
}
|
||||
|
|
@ -26,18 +26,28 @@ public partial class IndividualCollections
|
|||
return ret;
|
||||
}
|
||||
|
||||
public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage)
|
||||
public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage, int version)
|
||||
{
|
||||
if (_actors.Awaiter.IsCompletedSuccessfully)
|
||||
{
|
||||
var ret = ReadJObjectInternal(obj, storage);
|
||||
var ret = version switch
|
||||
{
|
||||
1 => ReadJObjectInternalV1(obj, storage),
|
||||
2 => ReadJObjectInternalV2(obj, storage),
|
||||
_ => true,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
|
||||
_actors.Awaiter.ContinueWith(_ =>
|
||||
{
|
||||
if (ReadJObjectInternal(obj, storage))
|
||||
if (version switch
|
||||
{
|
||||
1 => ReadJObjectInternalV1(obj, storage),
|
||||
2 => ReadJObjectInternalV2(obj, storage),
|
||||
_ => true,
|
||||
})
|
||||
saver.ImmediateSave(parent);
|
||||
IsLoaded = true;
|
||||
Loaded.Invoke();
|
||||
|
|
@ -45,7 +55,55 @@ public partial class IndividualCollections
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool ReadJObjectInternal(JArray? obj, CollectionStorage storage)
|
||||
private bool ReadJObjectInternalV1(JArray? obj, CollectionStorage storage)
|
||||
{
|
||||
Penumbra.Log.Debug("[Collections] Reading individual assignments...");
|
||||
if (obj == null)
|
||||
{
|
||||
Penumbra.Log.Debug($"[Collections] Finished reading {Count} individual assignments...");
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var data in obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
var identifier = _actors.FromJson(data as JObject);
|
||||
var group = GetGroup(identifier);
|
||||
if (group.Length == 0 || group.Any(i => !i.IsValid))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage("Could not load an unknown individual collection, removed.",
|
||||
NotificationType.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
var collectionName = data["Collection"]?.ToObject<string>() ?? string.Empty;
|
||||
if (collectionName.Length == 0 || !storage.ByName(collectionName, out var collection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Could not load the collection \"{collectionName}\" as individual collection for {identifier}, set to None.",
|
||||
NotificationType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Add(group, collection))
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage($"Could not add an individual collection for {identifier}, removed.",
|
||||
NotificationType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(e, $"Could not load an unknown individual collection, removed.", NotificationType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
Penumbra.Log.Debug($"Finished reading {Count} individual assignments...");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ReadJObjectInternalV2(JArray? obj, CollectionStorage storage)
|
||||
{
|
||||
Penumbra.Log.Debug("[Collections] Reading individual assignments...");
|
||||
if (obj == null)
|
||||
|
|
@ -64,17 +122,17 @@ public partial class IndividualCollections
|
|||
if (group.Length == 0 || group.Any(i => !i.IsValid))
|
||||
{
|
||||
changes = true;
|
||||
Penumbra.Messager.NotificationMessage("Could not load an unknown individual collection, removed.",
|
||||
Penumbra.Messager.NotificationMessage("Could not load an unknown individual collection, removed assignment.",
|
||||
NotificationType.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
var collectionName = data["Collection"]?.ToObject<string>() ?? string.Empty;
|
||||
if (collectionName.Length == 0 || !storage.ByName(collectionName, out var collection))
|
||||
var collectionId = data["Collection"]?.ToObject<Guid>();
|
||||
if (!collectionId.HasValue || !storage.ById(collectionId.Value, out var collection))
|
||||
{
|
||||
changes = true;
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Could not load the collection \"{collectionName}\" as individual collection for {identifier}, set to None.",
|
||||
$"Could not load the collection {collectionId} as individual collection for {identifier}, removed assignment.",
|
||||
NotificationType.Warning);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -82,14 +140,14 @@ public partial class IndividualCollections
|
|||
if (!Add(group, collection))
|
||||
{
|
||||
changes = true;
|
||||
Penumbra.Messager.NotificationMessage($"Could not add an individual collection for {identifier}, removed.",
|
||||
Penumbra.Messager.NotificationMessage($"Could not add an individual collection for {identifier}, removed assignment.",
|
||||
NotificationType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
changes = true;
|
||||
Penumbra.Messager.NotificationMessage(e, $"Could not load an unknown individual collection, removed.", NotificationType.Error);
|
||||
Penumbra.Messager.NotificationMessage(e, $"Could not load an unknown individual collection, removed assignment.", NotificationType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,14 +158,6 @@ public partial class IndividualCollections
|
|||
|
||||
internal void Migrate0To1(Dictionary<string, ModCollection> old)
|
||||
{
|
||||
static bool FindDataId(string name, NameDictionary data, out NpcId dataId)
|
||||
{
|
||||
var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase),
|
||||
new KeyValuePair<NpcId, string>(uint.MaxValue, string.Empty));
|
||||
dataId = kvp.Key;
|
||||
return kvp.Value.Length > 0;
|
||||
}
|
||||
|
||||
foreach (var (name, collection) in old)
|
||||
{
|
||||
var kind = ObjectKind.None;
|
||||
|
|
@ -155,5 +205,15 @@ public partial class IndividualCollections
|
|||
NotificationType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
static bool FindDataId(string name, NameDictionary data, out NpcId dataId)
|
||||
{
|
||||
var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase),
|
||||
new KeyValuePair<NpcId, string>(uint.MaxValue, string.Empty));
|
||||
dataId = kvp.Key;
|
||||
return kvp.Value.Length > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ public class InheritanceManager : IDisposable
|
|||
var changes = false;
|
||||
foreach (var subCollectionName in collection.InheritanceByName)
|
||||
{
|
||||
if (_storage.ByName(subCollectionName, out var subCollection))
|
||||
if (Guid.TryParse(subCollectionName, out var guid) && _storage.ById(guid, out var subCollection))
|
||||
{
|
||||
if (AddInheritance(collection, subCollection, false))
|
||||
continue;
|
||||
|
|
@ -146,6 +146,15 @@ public class InheritanceManager : IDisposable
|
|||
changes = true;
|
||||
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.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.");
|
||||
if (AddInheritance(collection, subCollection, false))
|
||||
continue;
|
||||
|
||||
Penumbra.Messager.NotificationMessage($"{collection.Name} can not inherit from {subCollection.Name}, removed.", NotificationType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using OtterGui;
|
||||
using Penumbra.Api;
|
||||
using Penumbra.Communication;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
|
@ -9,13 +10,13 @@ namespace Penumbra.Collections.Manager;
|
|||
|
||||
public class TempCollectionManager : IDisposable
|
||||
{
|
||||
public int GlobalChangeCounter { get; private set; } = 0;
|
||||
public int GlobalChangeCounter { get; private set; }
|
||||
public readonly IndividualCollections Collections;
|
||||
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CollectionStorage _storage;
|
||||
private readonly ActorManager _actors;
|
||||
private readonly Dictionary<string, ModCollection> _customCollections = new();
|
||||
private readonly CommunicatorService _communicator;
|
||||
private readonly CollectionStorage _storage;
|
||||
private readonly ActorManager _actors;
|
||||
private readonly Dictionary<Guid, ModCollection> _customCollections = [];
|
||||
|
||||
public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorManager actors, CollectionStorage storage)
|
||||
{
|
||||
|
|
@ -42,36 +43,36 @@ public class TempCollectionManager : IDisposable
|
|||
=> _customCollections.Values;
|
||||
|
||||
public bool CollectionByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
||||
=> _customCollections.TryGetValue(name.ToLowerInvariant(), out collection);
|
||||
=> _customCollections.Values.FindFirst(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase), out collection);
|
||||
|
||||
public string CreateTemporaryCollection(string name)
|
||||
public bool CollectionById(Guid id, [NotNullWhen(true)] out ModCollection? collection)
|
||||
=> _customCollections.TryGetValue(id, out collection);
|
||||
|
||||
public Guid CreateTemporaryCollection(string name)
|
||||
{
|
||||
if (_storage.ByName(name, out _))
|
||||
return string.Empty;
|
||||
|
||||
if (GlobalChangeCounter == int.MaxValue)
|
||||
GlobalChangeCounter = 0;
|
||||
var collection = ModCollection.CreateTemporary(name, ~Count, GlobalChangeCounter++);
|
||||
Penumbra.Log.Debug($"Creating temporary collection {collection.AnonymizedName}.");
|
||||
if (_customCollections.TryAdd(collection.Name.ToLowerInvariant(), collection))
|
||||
Penumbra.Log.Debug($"Creating temporary collection {collection.Name} with {collection.Id}.");
|
||||
if (_customCollections.TryAdd(collection.Id, collection))
|
||||
{
|
||||
// Temporary collection created.
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, null, collection, string.Empty);
|
||||
return collection.Name;
|
||||
return collection.Id;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
public bool RemoveTemporaryCollection(string collectionName)
|
||||
public bool RemoveTemporaryCollection(Guid collectionId)
|
||||
{
|
||||
if (!_customCollections.Remove(collectionName.ToLowerInvariant(), out var collection))
|
||||
if (!_customCollections.Remove(collectionId, out var collection))
|
||||
{
|
||||
Penumbra.Log.Debug($"Tried to delete temporary collection {collectionName.ToLowerInvariant()}, but did not exist.");
|
||||
Penumbra.Log.Debug($"Tried to delete temporary collection {collectionId}, but did not exist.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Penumbra.Log.Debug($"Deleted temporary collection {collection.AnonymizedName}.");
|
||||
Penumbra.Log.Debug($"Deleted temporary collection {collection.Id}.");
|
||||
GlobalChangeCounter += Math.Max(collection.ChangeCounter + 1 - GlobalChangeCounter, 0);
|
||||
for (var i = 0; i < Collections.Count; ++i)
|
||||
{
|
||||
|
|
@ -80,7 +81,7 @@ public class TempCollectionManager : IDisposable
|
|||
|
||||
// Temporary collection assignment removed.
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Temporary, collection, null, Collections[i].DisplayName);
|
||||
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.AnonymizedName} from {Collections[i].DisplayName}.");
|
||||
Penumbra.Log.Verbose($"Unassigned temporary collection {collection.Id} from {Collections[i].DisplayName}.");
|
||||
Collections.Delete(i--);
|
||||
}
|
||||
|
||||
|
|
@ -98,32 +99,32 @@ public class TempCollectionManager : IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool AddIdentifier(string collectionName, params ActorIdentifier[] identifiers)
|
||||
public bool AddIdentifier(Guid collectionId, params ActorIdentifier[] identifiers)
|
||||
{
|
||||
if (!_customCollections.TryGetValue(collectionName.ToLowerInvariant(), out var collection))
|
||||
if (!_customCollections.TryGetValue(collectionId, out var collection))
|
||||
return false;
|
||||
|
||||
return AddIdentifier(collection, identifiers);
|
||||
}
|
||||
|
||||
public bool AddIdentifier(string collectionName, string characterName, ushort worldId = ushort.MaxValue)
|
||||
public bool AddIdentifier(Guid collectionId, string characterName, ushort worldId = ushort.MaxValue)
|
||||
{
|
||||
if (!ByteString.FromString(characterName, out var byteString, false))
|
||||
if (!ByteString.FromString(characterName, out var byteString))
|
||||
return false;
|
||||
|
||||
var identifier = _actors.CreatePlayer(byteString, worldId);
|
||||
if (!identifier.IsValid)
|
||||
return false;
|
||||
|
||||
return AddIdentifier(collectionName, identifier);
|
||||
return AddIdentifier(collectionId, identifier);
|
||||
}
|
||||
|
||||
internal bool RemoveByCharacterName(string characterName, ushort worldId = ushort.MaxValue)
|
||||
{
|
||||
if (!ByteString.FromString(characterName, out var byteString, false))
|
||||
if (!ByteString.FromString(characterName, out var byteString))
|
||||
return false;
|
||||
|
||||
var identifier = _actors.CreatePlayer(byteString, worldId);
|
||||
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Name);
|
||||
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue