Rework API, use Collection ID in crash handler, use collection GUIDs in more places.

This commit is contained in:
Ottermandias 2024-04-12 12:33:57 +02:00
parent 793ed4f0a7
commit ba8999914f
88 changed files with 4193 additions and 3930 deletions

View file

@ -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;
}

View file

@ -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);
}

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.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;
}
}
}

View file

@ -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(

View file

@ -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);
}
}