Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Prefabs/PrefabSelector.cs
Juan Pablo Arce 1fd2a51bbb Unstable v0.19.5.0
2022-09-14 12:48:12 -03:00

174 lines
5.8 KiB
C#

#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Barotrauma.Threading;
namespace Barotrauma
{
public class PrefabSelector<T> : IEnumerable<T> where T : notnull, Prefab
{
private readonly ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
public T? BasePrefab
{
get
{
using (new ReadLock(rwl)) { return basePrefabInternal; }
}
}
public T? ActivePrefab
{
get
{
using (new ReadLock(rwl)) { return activePrefabInternal; }
}
}
public void Add(T prefab, bool isOverride)
{
using (new WriteLock(rwl)) { AddInternal(prefab, isOverride); }
}
public void RemoveIfContains(T prefab)
{
using (new WriteLock(rwl)) { RemoveIfContainsInternal(prefab); }
}
public void Remove(T prefab)
{
using (new WriteLock(rwl)) { RemoveInternal(prefab); }
}
public void RemoveByFile(ContentFile file, Action<T>? callback = null)
{
var removed = new List<T>();
using (new WriteLock(rwl))
{
for (int i = overrides.Count-1; i >= 0; i--)
{
var prefab = overrides[i];
if (prefab.ContentFile == file)
{
RemoveInternal(prefab);
removed.Add(prefab);
}
}
if (basePrefabInternal is { ContentFile: var baseFile } p && baseFile == file)
{
RemoveInternal(basePrefabInternal);
removed.Add(p);
}
}
if (callback != null) { removed.ForEach(callback); }
}
public void Sort()
{
using (new WriteLock(rwl)) { SortInternal(); }
}
public bool IsEmpty
{
get
{
using (new ReadLock(rwl)) { return isEmptyInternal; }
}
}
public bool Contains(T prefab)
{
using (new ReadLock(rwl)) { return ContainsInternal(prefab); }
}
public bool IsOverride(T prefab)
{
using (new ReadLock(rwl)) { return IsOverrideInternal(prefab); }
}
#region Underlying implementations of the public methods, done separately to avoid nested locking
private T? basePrefabInternal;
private readonly List<T> overrides = new List<T>();
private T? activePrefabInternal => overrides.Count > 0 ? overrides.First() : basePrefabInternal;
private void AddInternal(T prefab, bool isOverride)
{
if (isOverride)
{
if (overrides.Contains(prefab)) { throw new InvalidOperationException($"Duplicate prefab in PrefabSelector ({typeof(T)}, {prefab.Identifier}, {prefab.ContentFile.ContentPackage.Name})"); }
overrides.Add(prefab);
}
else
{
if (basePrefabInternal != null)
{
string prefabName
= prefab is MapEntityPrefab mapEntityPrefab
? $"\"{mapEntityPrefab.OriginalName}\", \"{prefab.Identifier}\""
: $"\"{prefab.Identifier}\"";
throw new InvalidOperationException(
$"Failed to add the prefab {prefabName} ({prefab.GetType()}) from \"{prefab.ContentPackage?.Name ?? "[NULL]"}\" ({prefab.ContentPackage?.Dir ?? ""}): "
+ $"a prefab with the same identifier from \"{activePrefabInternal!.ContentPackage?.Name ?? "[NULL]"}\" ({activePrefabInternal!.ContentPackage?.Dir ?? ""}) already exists; try overriding");
}
basePrefabInternal = prefab;
}
SortInternal();
}
private void RemoveIfContainsInternal(T prefab)
{
if (!ContainsInternal(prefab)) { return; }
RemoveInternal(prefab);
}
private void RemoveInternal(T prefab)
{
if (basePrefabInternal == prefab) { basePrefabInternal = null; }
else if (overrides.Contains(prefab)) { overrides.Remove(prefab); }
else { throw new InvalidOperationException($"Can't remove prefab from PrefabSelector ({typeof(T)}, {prefab.Identifier}, {prefab.ContentFile.ContentPackage.Name})"); }
prefab.Dispose();
SortInternal();
}
private void SortInternal()
{
overrides.Sort((p1, p2) => (p1.ContentPackage?.Index ?? int.MaxValue) - (p2.ContentPackage?.Index ?? int.MaxValue));
}
private bool isEmptyInternal => basePrefabInternal is null && overrides.Count == 0;
private bool ContainsInternal(T prefab) => basePrefabInternal == prefab || overrides.Contains(prefab);
private int IndexOfInternal(T prefab) => basePrefabInternal == prefab
? overrides.Count
: overrides.IndexOf(prefab);
private bool IsOverrideInternal(T prefab) => IndexOfInternal(prefab) > 0;
#endregion
public IEnumerator<T> GetEnumerator()
{
T? basePrefab;
ImmutableArray<T> overrideClone;
using (new ReadLock(rwl))
{
basePrefab = basePrefabInternal;
overrideClone = overrides.ToImmutableArray();
}
if (basePrefab != null) { yield return basePrefab; }
foreach (T prefab in overrideClone)
{
yield return prefab;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}