diff --git a/MonkeyLoader/MonkeyLoader.csproj b/MonkeyLoader/MonkeyLoader.csproj
index 9426542..8a34122 100644
--- a/MonkeyLoader/MonkeyLoader.csproj
+++ b/MonkeyLoader/MonkeyLoader.csproj
@@ -11,7 +11,7 @@
True
MonkeyLoader
Banane9
- 0.11.0-beta
+ 0.12.0-beta
A convenience and extendability focused mod loader using NuGet packages.
README.md
LGPL-3.0-or-later
diff --git a/MonkeyLoader/SortedCollection.cs b/MonkeyLoader/SortedCollection.cs
new file mode 100644
index 0000000..946ca5e
--- /dev/null
+++ b/MonkeyLoader/SortedCollection.cs
@@ -0,0 +1,220 @@
+using HarmonyLib;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MonkeyLoader
+{
+ ///
+ /// Represents a collection which is kept sorted using an
+ /// or the default comparer for .
+ ///
+ ///
+ /// Duplicate items, null items, and multiple items comparing equal are supported.
+ ///
+ /// The type of the items.
+ public class SortedCollection : ICollection
+ {
+ private readonly IComparer _comparer;
+ private readonly List _values;
+
+ ///
+ public int Count => _values.Count;
+
+ bool ICollection.IsReadOnly => ((ICollection)_values).IsReadOnly;
+
+ ///
+ /// Gets the element at the specified index in the collection.
+ ///
+ /// The index to get the element at.
+ /// The element at the specified index.
+ /// When the index is not in the collection.
+ public T this[int index] => _values[index];
+
+ ///
+ /// Constructs an empty sorted collection with the
+ /// default comparer for .
+ ///
+ /// If is not .
+ public SortedCollection() : this(null, null)
+ { }
+
+ ///
+ /// Constructs an empty sorted collection with the given comparer to keep it sorted.
+ ///
+ /// The comparer to use to keep the collection sorted.
+ /// If is null and is not .
+ public SortedCollection(IComparer? comparer) : this(null, comparer)
+ { }
+
+ ///
+ /// Constructs a sorted collection with the given elements and comparer to keep it sorted.
+ ///
+ /// The elements to add to the collection.
+ /// The comparer to use to keep the collection sorted.
+ /// If is null and is not .
+ public SortedCollection(IEnumerable? collection, IComparer? comparer)
+ {
+ if (comparer is null && !typeof(IComparable).IsAssignableFrom(typeof(T)))
+ throw new NotSupportedException("Can't create a sorted collection of an uncomparable type without a comparer!");
+
+ _comparer = comparer ?? Comparer.Default;
+
+ _values = new(collection ?? Array.Empty());
+ _values.Sort(_comparer);
+ }
+
+ ///
+ /// Constructs a sorted collection with the given elements and
+ /// default comparer for .
+ ///
+ /// The elements to add to the collection.
+ /// If is not .
+ public SortedCollection(IEnumerable? collection) : this(collection, null)
+ { }
+
+ ///
+ public void Add(T item) => _values.Insert(FindInsertIndex(item), item);
+
+ ///
+ /// Adds each item in the given sequence to this sorted collection.
+ ///
+ /// The collection of items to add.
+ public void AddRange(IEnumerable collection)
+ => collection?.Do(Add);
+
+ ///
+ public void Clear() => _values.Clear();
+
+ ///
+ public bool Contains(T item) => _values.IndexOf(item) != -1;
+
+ ///
+ public void CopyTo(T[] array, int arrayIndex) => _values.CopyTo(array, arrayIndex);
+
+ ///
+ public IEnumerator GetEnumerator() => _values.GetEnumerator();
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_values).GetEnumerator();
+
+ ///
+ /// Creates a shallow copy of a range of elements this sorted collection.
+ ///
+ /// The zero-based index at which the range starts.
+ /// The number of elements in the range.
+ /// A shallow copy of a range of elements in this sorted collection.
+ /// is less than 0. -or- is less than 0.
+ /// and do not denote a valid range of elements in this sorted collection.
+ public SortedCollection GetRange(int index, int count)
+ => new(_values.GetRange(index, count), _comparer);
+
+ ///
+ /// Attempts to find the first index of the given item.
+ ///
+ /// The item to look for.
+ /// The first index of the item if it was found; otherwise, -1.
+ public int IndexOf(T item)
+ {
+ var i = ÍndexOfEqualityClass(item);
+
+ if (i < 0)
+ return -1;
+
+ // search forwards
+ for (; i < _values.Count && _comparer.Compare(item, _values[i]) == 0; ++i)
+ {
+ if (item?.Equals(_values[i]) ?? _values[i] is null)
+ return i;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Attempts to find the first index in this collection with an item that compares equal to the needle.
+ ///
+ /// The item to search for.
+ /// The last index if equal items are found; otherwise, -1.
+ public int ÍndexOfEqualityClass(T needle)
+ {
+ var i = IndexInEqualityClass(needle);
+
+ if (i < 0)
+ return -1;
+
+ // search backwards
+ while (--i >= 0 && _comparer.Compare(needle, _values[i]) == 0)
+ { }
+
+ return ++i;
+ }
+
+ ///
+ /// Attempts to find the last index of the given item.
+ ///
+ /// The item to look for.
+ /// The last index of the item if it was found; otherwise, -1.
+ public int LastIndexOf(T item)
+ {
+ var i = LastIndexOfEqualityClass(item);
+
+ if (i < 0)
+ return -1;
+
+ // search backwards
+ for (; i >= 0 && _comparer.Compare(item, _values[i]) == 0; --i)
+ {
+ if (item?.Equals(_values[i]) ?? _values[i] is null)
+ return i;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Attempts to find the last index in this collection with an item that compares equal to the needle.
+ ///
+ /// The item to search for.
+ /// The last index if equal items are found; otherwise, -1.
+ public int LastIndexOfEqualityClass(T needle)
+ {
+ var i = IndexInEqualityClass(needle);
+
+ if (i < 0)
+ return -1;
+
+ // search forwards
+ while (++i < _values.Count && _comparer.Compare(needle, _values[i]) == 0)
+ { }
+
+ return --i;
+ }
+
+ ///
+ public bool Remove(T item) => _values.Remove(item);
+
+ ///
+ /// Removes the element at the given index.
+ ///
+ /// The index of the item to remove.
+ /// When the index is not in the collection.
+ public void RemoveAt(int index) => _values.RemoveAt(index);
+
+ private int FindInsertIndex(T needle)
+ {
+ var i = IndexInEqualityClass(needle);
+ if (i < 0)
+ return ~i;
+
+ // search forwards
+ while (++i < _values.Count && _comparer.Compare(needle, _values[i]) == 0)
+ { }
+
+ return i;
+ }
+
+ private int IndexInEqualityClass(T needle) => _values.BinarySearch(needle, _comparer);
+ }
+}
\ No newline at end of file