mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Merge c42f89277c into ccb5b01290
This commit is contained in:
commit
66282b73fa
1 changed files with 105 additions and 50 deletions
|
|
@ -31,69 +31,102 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
|
||||
public ModCollection Create(string name, int index, ModCollection? duplicate)
|
||||
{
|
||||
var newCollection = duplicate?.Duplicate(name, CurrentCollectionId, index)
|
||||
?? ModCollection.CreateEmpty(name, CurrentCollectionId, index, _modStorage.Count);
|
||||
_collectionsByLocal[CurrentCollectionId] = newCollection;
|
||||
CurrentCollectionId += 1;
|
||||
var localId = AllocateNextId();
|
||||
var newCollection = duplicate?.Duplicate(name, localId, index)
|
||||
?? ModCollection.CreateEmpty(name, localId, index, _modStorage.Count);
|
||||
AddAtLocalId(newCollection, localId);
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
public ModCollection CreateFromData(Guid id, string name, int version, Dictionary<string, ModSettings.SavedSettings> allSettings,
|
||||
IReadOnlyList<string> inheritances)
|
||||
{
|
||||
var localId = AllocateNextId();
|
||||
var newCollection = ModCollection.CreateFromData(_saveService, _modStorage,
|
||||
new ModCollectionIdentity(id, CurrentCollectionId, name, Count), version, allSettings, inheritances);
|
||||
_collectionsByLocal[CurrentCollectionId] = newCollection;
|
||||
CurrentCollectionId += 1;
|
||||
new ModCollectionIdentity(id, localId, name, Count), version, allSettings, inheritances);
|
||||
AddAtLocalId(newCollection, localId);
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
public ModCollection CreateTemporary(string name, int index, int globalChangeCounter)
|
||||
{
|
||||
var newCollection = ModCollection.CreateTemporary(name, CurrentCollectionId, index, globalChangeCounter);
|
||||
_collectionsByLocal[CurrentCollectionId] = newCollection;
|
||||
CurrentCollectionId += 1;
|
||||
var localId = AllocateNextId();
|
||||
var newCollection = ModCollection.CreateTemporary(name, localId, index, globalChangeCounter);
|
||||
AddAtLocalId(newCollection, localId);
|
||||
return newCollection;
|
||||
}
|
||||
|
||||
/// <remarks> Atomically add to _collectionLocal at the id given. </remarks>
|
||||
private void AddAtLocalId(ModCollection newCollection, LocalCollectionId id)
|
||||
{
|
||||
_collectionsByLocal.AddOrUpdate(id,
|
||||
static (_, newColl) => newColl,
|
||||
static (_, _, newColl) => newColl,
|
||||
newCollection);
|
||||
}
|
||||
|
||||
public void Delete(ModCollection collection)
|
||||
=> _collectionsByLocal.Remove(collection.Identity.LocalId);
|
||||
=> _collectionsByLocal.TryRemove(collection.Identity.LocalId, out _);
|
||||
|
||||
/// <remarks> The empty collection is always available at Index 0. </remarks>
|
||||
private readonly List<ModCollection> _collections =
|
||||
[
|
||||
ModCollection.Empty,
|
||||
];
|
||||
|
||||
private readonly Lock _collectionsLock = new();
|
||||
|
||||
/// <remarks> A list of all collections ever created still existing by their local id. </remarks>
|
||||
private readonly Dictionary<LocalCollectionId, ModCollection>
|
||||
private readonly ConcurrentDictionary<LocalCollectionId, ModCollection>
|
||||
_collectionsByLocal = new() { [LocalCollectionId.Zero] = ModCollection.Empty };
|
||||
|
||||
|
||||
public readonly ModCollection DefaultNamed;
|
||||
|
||||
/// <remarks> Incremented by 1 because the empty collection gets Zero. </remarks>
|
||||
public LocalCollectionId CurrentCollectionId { get; private set; } = LocalCollectionId.Zero + 1;
|
||||
/// <remarks> Starts at 1 because the empty collection gets Zero. </remarks>
|
||||
private int _currentCollectionIdValue = 1;
|
||||
|
||||
/// <remarks> Starts at 1 because the empty collection gets Zero. </remarks>
|
||||
public LocalCollectionId CurrentCollectionId => new(_currentCollectionIdValue);
|
||||
|
||||
private LocalCollectionId AllocateNextId()
|
||||
{
|
||||
var newLocalId = new LocalCollectionId(_currentCollectionIdValue);
|
||||
Interlocked.Increment(ref _currentCollectionIdValue);
|
||||
return newLocalId;
|
||||
}
|
||||
|
||||
/// <summary> Default enumeration skips the empty collection. </summary>
|
||||
public IEnumerator<ModCollection> GetEnumerator()
|
||||
=> _collections.Skip(1).GetEnumerator();
|
||||
=> GetModSnapShot().ToList().GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _collections.Count;
|
||||
{
|
||||
get
|
||||
{
|
||||
lock(_collectionsLock)
|
||||
return _collections.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public ModCollection this[int index]
|
||||
=> _collections[index];
|
||||
{
|
||||
get
|
||||
{
|
||||
lock(_collectionsLock)
|
||||
return _collections[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Find a collection by its name. If the name is empty or None, the empty collection is returned. </summary>
|
||||
public bool ByName(string name, [NotNullWhen(true)] out ModCollection? collection)
|
||||
{
|
||||
if (name.Length != 0)
|
||||
return _collections.FindFirst(c => string.Equals(c.Identity.Name, name, StringComparison.OrdinalIgnoreCase), out collection);
|
||||
|
||||
lock(_collectionsLock)
|
||||
return _collections.FindFirst(c => string.Equals(c.Identity.Name, name, StringComparison.OrdinalIgnoreCase), out collection);
|
||||
collection = ModCollection.Empty;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -102,8 +135,8 @@ 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.Identity.Id == id, out collection);
|
||||
|
||||
lock(_collectionsLock)
|
||||
return _collections.FindFirst(c => c.Identity.Id == id, out collection);
|
||||
collection = ModCollection.Empty;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -155,8 +188,12 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
if (name.Length == 0)
|
||||
return false;
|
||||
|
||||
var newCollection = Create(name, _collections.Count, duplicate);
|
||||
_collections.Add(newCollection);
|
||||
ModCollection newCollection;
|
||||
lock (_collectionsLock)
|
||||
{
|
||||
newCollection = Create(name, _collections.Count, duplicate);
|
||||
_collections.Add(newCollection);
|
||||
}
|
||||
_saveService.ImmediateSave(new ModCollectionSave(_modStorage, newCollection));
|
||||
Penumbra.Messager.NotificationMessage($"Created new collection {newCollection.Identity.AnonymizedName}.", NotificationType.Success, false);
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Inactive, null, newCollection, string.Empty);
|
||||
|
|
@ -168,25 +205,28 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
/// </summary>
|
||||
public bool RemoveCollection(ModCollection collection)
|
||||
{
|
||||
if (collection.Identity.Index <= ModCollection.Empty.Identity.Index || collection.Identity.Index >= _collections.Count)
|
||||
lock (_collectionsLock)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage("Can not remove the empty collection.", NotificationType.Error, false);
|
||||
return false;
|
||||
}
|
||||
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.Identity.Index == DefaultNamed.Identity.Index)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
|
||||
return false;
|
||||
}
|
||||
if (collection.Identity.Index == DefaultNamed.Identity.Index)
|
||||
{
|
||||
Penumbra.Messager.NotificationMessage("Can not remove the default collection.", NotificationType.Error, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
Delete(collection);
|
||||
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
|
||||
_collections.RemoveAt(collection.Identity.Index);
|
||||
// Update indices.
|
||||
for (var i = collection.Identity.Index; i < Count; ++i)
|
||||
_collections[i].Identity.Index = i;
|
||||
_collectionsByLocal.Remove(collection.Identity.LocalId);
|
||||
Delete(collection);
|
||||
_saveService.ImmediateDelete(new ModCollectionSave(_modStorage, collection));
|
||||
|
||||
_collections.RemoveAt(collection.Identity.Index);
|
||||
// Update indices.
|
||||
for (var i = collection.Identity.Index; i < _collections.Count; ++i)
|
||||
_collections[i].Identity.Index = i;
|
||||
}
|
||||
|
||||
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.Identity.AnonymizedName}.", NotificationType.Success, false);
|
||||
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);
|
||||
|
|
@ -299,26 +339,28 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
return collection;
|
||||
|
||||
if (AddCollection(ModCollectionIdentity.DefaultCollectionName, null))
|
||||
return _collections[^1];
|
||||
return this[^1];
|
||||
|
||||
Penumbra.Messager.NotificationMessage(
|
||||
$"Unknown problem creating a collection with the name {ModCollectionIdentity.DefaultCollectionName}, which is required to exist.",
|
||||
NotificationType.Error);
|
||||
return Count > 1 ? _collections[1] : _collections[0];
|
||||
return Count > 1 ? this[1] : this[0];
|
||||
}
|
||||
|
||||
/// <summary> Move all settings in all collections to unused settings. </summary>
|
||||
private void OnModDiscoveryStarted()
|
||||
{
|
||||
foreach (var collection in this)
|
||||
var snapshot = GetModSnapShot();
|
||||
foreach (var collection in snapshot)
|
||||
collection.Settings.PrepareModDiscovery(_modStorage);
|
||||
}
|
||||
|
||||
/// <summary> Restore all settings in all collections to mods. </summary>
|
||||
private void OnModDiscoveryFinished()
|
||||
{
|
||||
var snapshot = GetModSnapShot();
|
||||
// Re-apply all mod settings.
|
||||
foreach (var collection in this)
|
||||
foreach (var collection in snapshot)
|
||||
collection.Settings.ApplyModSettings(collection, _saveService, _modStorage);
|
||||
}
|
||||
|
||||
|
|
@ -326,22 +368,23 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
private void OnModPathChange(ModPathChangeType type, Mod mod, DirectoryInfo? oldDirectory,
|
||||
DirectoryInfo? newDirectory)
|
||||
{
|
||||
var snapshot = GetModSnapShot();
|
||||
switch (type)
|
||||
{
|
||||
case ModPathChangeType.Added:
|
||||
foreach (var collection in this)
|
||||
foreach (var collection in snapshot)
|
||||
collection.Settings.AddMod(mod);
|
||||
break;
|
||||
case ModPathChangeType.Deleted:
|
||||
foreach (var collection in this)
|
||||
foreach (var collection in snapshot)
|
||||
collection.Settings.RemoveMod(mod);
|
||||
break;
|
||||
case ModPathChangeType.Moved:
|
||||
foreach (var collection in this.Where(collection => collection.GetOwnSettings(mod.Index) != null))
|
||||
foreach (var collection in snapshot.Where(collection => collection.GetOwnSettings(mod.Index) != null))
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
break;
|
||||
case ModPathChangeType.Reloaded:
|
||||
foreach (var collection in this)
|
||||
foreach (var collection in snapshot)
|
||||
{
|
||||
if (collection.GetOwnSettings(mod.Index)?.Settings.FixAll(mod) ?? false)
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
|
|
@ -359,8 +402,9 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
type.HandlingInfo(out var requiresSaving, out _, out _);
|
||||
if (!requiresSaving)
|
||||
return;
|
||||
|
||||
foreach (var collection in this)
|
||||
|
||||
var snapshot = GetModSnapShot();
|
||||
foreach (var collection in snapshot)
|
||||
{
|
||||
if (collection.GetOwnSettings(mod.Index)?.HandleChanges(type, mod, group, option, movedToIdx) ?? false)
|
||||
_saveService.QueueSave(new ModCollectionSave(_modStorage, collection));
|
||||
|
|
@ -374,11 +418,22 @@ public class CollectionStorage : IReadOnlyList<ModCollection>, IDisposable, ISer
|
|||
if (file.CurrentUsage == 0)
|
||||
return;
|
||||
|
||||
foreach (var collection in this)
|
||||
var snapshot = GetModSnapShot();
|
||||
foreach (var collection in snapshot)
|
||||
{
|
||||
var (settings, _) = collection.GetActualSettings(mod.Index);
|
||||
if (settings is { Enabled: true })
|
||||
collection.Counters.IncrementChange();
|
||||
}
|
||||
}
|
||||
|
||||
private ModCollection[] GetModSnapShot()
|
||||
{
|
||||
ModCollection[] snapshot;
|
||||
lock (_collectionsLock)
|
||||
{
|
||||
snapshot = _collections.Skip(1).ToArray();
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue