3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
7 using System.Threading;
14 private readonly ReaderWriterLockSlim rwl =
new ReaderWriterLockSlim();
20 using (
new ReadLock(rwl)) {
return basePrefabInternal; }
28 using (
new ReadLock(rwl)) {
return activePrefabInternal; }
32 public void Add(T prefab,
bool isOverride)
34 using (
new WriteLock(rwl)) { AddInternal(prefab, isOverride); }
39 using (
new WriteLock(rwl)) { RemoveIfContainsInternal(prefab); }
44 using (
new WriteLock(rwl)) { RemoveInternal(prefab); }
49 var removed =
new List<T>();
50 using (
new WriteLock(rwl))
52 for (
int i = overrides.Count-1; i >= 0; i--)
54 var prefab = overrides[i];
55 if (prefab.ContentFile == file)
57 RemoveInternal(prefab);
62 if (basePrefabInternal is {
ContentFile: var baseFile } p && baseFile == file)
64 RemoveInternal(basePrefabInternal);
68 if (callback !=
null) { removed.ForEach(callback); }
73 using (
new WriteLock(rwl)) { SortInternal(); }
80 using (
new ReadLock(rwl)) {
return isEmptyInternal; }
86 using (
new ReadLock(rwl)) {
return ContainsInternal(prefab); }
91 using (
new ReadLock(rwl)) {
return IsOverrideInternal(prefab); }
95 #region Underlying implementations of the public methods, done separately to avoid nested locking
96 private T? basePrefabInternal;
97 private readonly List<T> overrides =
new List<T>();
99 private T? activePrefabInternal => overrides.Count > 0 ? overrides.First() : basePrefabInternal;
101 private void AddInternal(T prefab,
bool isOverride)
105 if (overrides.Contains(prefab)) {
throw new InvalidOperationException($
"Duplicate prefab in PrefabSelector ({typeof(T)}, {prefab.Identifier}, {prefab.ContentFile.ContentPackage.Name})"); }
106 overrides.Add(prefab);
110 if (basePrefabInternal !=
null)
113 = prefab is MapEntityPrefab mapEntityPrefab
114 ? $
"\"{mapEntityPrefab.OriginalName}\", \"{prefab.Identifier}\""
115 : $
"\"{prefab.Identifier}\"";
116 throw new InvalidOperationException(
117 $
"Failed to add the prefab {prefabName} ({prefab.GetType()}) from \"{prefab.ContentPackage?.Name ?? "[NULL]
"}\" ({prefab.ContentPackage?.Dir ?? ""}): "
118 + $
"a prefab with the same identifier from \"{activePrefabInternal!.ContentPackage?.Name ?? "[NULL]
"}\" ({activePrefabInternal!.ContentPackage?.Dir ?? ""}) already exists; try overriding");
120 basePrefabInternal = prefab;
125 private void RemoveIfContainsInternal(T prefab)
127 if (!ContainsInternal(prefab)) {
return; }
128 RemoveInternal(prefab);
131 private void RemoveInternal(T prefab)
133 if (basePrefabInternal == prefab) { basePrefabInternal =
null; }
134 else if (overrides.Contains(prefab)) { overrides.Remove(prefab); }
135 else {
throw new InvalidOperationException($
"Can't remove prefab from PrefabSelector ({typeof(T)}, {prefab.Identifier}, {prefab.ContentFile.ContentPackage.Name})"); }
140 private void SortInternal()
142 overrides.Sort((p1, p2) => (p1.ContentPackage?.Index ??
int.MaxValue) - (p2.ContentPackage?.Index ??
int.MaxValue));
145 private bool isEmptyInternal => basePrefabInternal is
null && overrides.Count == 0;
147 private bool ContainsInternal(T prefab) => basePrefabInternal == prefab || overrides.Contains(prefab);
149 private int IndexOfInternal(T prefab) => basePrefabInternal == prefab
151 : overrides.IndexOf(prefab);
153 private bool IsOverrideInternal(T prefab) => IndexOfInternal(prefab) > 0;
159 ImmutableArray<T> overrideClone;
160 using (
new ReadLock(rwl))
162 basePrefab = basePrefabInternal;
163 overrideClone = overrides.ToImmutableArray();
165 if (basePrefab !=
null) { yield
return basePrefab; }
166 foreach (T prefab
in overrideClone)
Base class for content file types, which are loaded from filelist.xml via reflection....
IEnumerator< T > GetEnumerator()
bool IsOverride(T prefab)
void Add(T prefab, bool isOverride)
void RemoveByFile(ContentFile file, Action< T >? callback=null)
void RemoveIfContains(T prefab)