This commit is contained in:
neuecc 2021-09-02 17:53:33 +09:00
parent cfaefc4454
commit 6a86e8c267
97 changed files with 7493 additions and 26 deletions

34
.gitignore vendored
View File

@ -117,29 +117,11 @@ nuget/*.unitypackage
# Unity
src/MessagePipe.Unity/Library/*
src/MessagePipe.Unity/Temp/*
src/MessagePipe.Unity/Logs/*
src/MessagePipe.Unity/Assembly-CSharp.csproj
src/MessagePipe.Unity/MessagePipe.csproj
src/MessagePipe.Unity/MessagePipe.Unity.sln
src/MessagePipe.Unity/MessagePipe.VContainer.csproj
src/MessagePipe.Unity/RuntimeUnitTestToolkit.csproj
src/MessagePipe.Unity/Tests.csproj
src/MessagePipe.Unity/Assembly-CSharp-firstpass.csproj
src/MessagePipe.Unity/MessagePipe.Zenject.csproj
src/MessagePipe.Unity/Assembly-CSharp-Editor.csproj
src/MessagePipe.Unity/Zenject-Editor.csproj
src/MessagePipe.Unity/Zenject.csproj
src/MessagePipe.Unity/MessagePipe.Editor.csproj
src/ObservableCollections.Unity/Library/*
src/ObservableCollections.Unity/Temp/*
src/ObservableCollections.Unity/Logs/*
src/ObservableCollections.Unity/Assembly-CSharp.csproj
src/ObservableCollections.Unity/ObservableCollections.csproj
src/ObservableCollections.Unity/ObservableCollections.Unity.sln
src/ObservableCollections.Unity/Assembly-CSharp-firstpass.csproj
src/ObservableCollections.Unity/Assembly-CSharp-Editor.csproj

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 58b896faeb4c95c4a939b19f8dbf74ab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 791bdc9996d0f5c42bea2aa68c95dced
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2ca402f41ce60d4c95ee34b6715ee8a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
public sealed class FreezedDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>, IFreezedCollection<KeyValuePair<TKey, TValue>>
{
readonly IReadOnlyDictionary<TKey, TValue> dictionary;
public FreezedDictionary(IReadOnlyDictionary<TKey, TValue> dictionary)
{
this.dictionary = dictionary;
}
public TValue this[TKey key] => dictionary[key];
public IEnumerable<TKey> Keys => dictionary.Keys;
public IEnumerable<TValue> Values => dictionary.Values;
public int Count => dictionary.Count;
public bool ContainsKey(TKey key)
{
return dictionary.ContainsKey(key);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return dictionary.GetEnumerator();
}
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
return dictionary.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)dictionary).GetEnumerator();
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool reverse = false)
{
return new FreezedView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform, reverse);
}
public ISortableSynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortableView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform)
{
return new FreezedSortableView<KeyValuePair<TKey, TValue>, TView>(dictionary, transform);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1470201cf9f7db249b7f441e515b77b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,61 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace ObservableCollections
{
public sealed class FreezedList<T> : IReadOnlyList<T>, IFreezedCollection<T>
{
readonly IReadOnlyList<T> list;
public T this[int index]
{
get
{
return list[index];
}
}
public int Count
{
get
{
return list.Count;
}
}
public bool IsReadOnly => true;
public FreezedList(IReadOnlyList<T> list)
{
this.list = list;
}
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new FreezedView<T, TView>(list, transform, reverse);
}
public ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform)
{
return new FreezedSortableView<T, TView>(list, transform);
}
public bool Contains(T item)
{
return list.Contains(item);
}
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3207eb016a325514b8f137b4cf17df40
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,118 @@
using ObservableCollections.Internal;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
namespace ObservableCollections
{
public delegate void NotifyCollectionChangedEventHandler<T>(in NotifyCollectionChangedEventArgs<T> e);
public interface IObservableCollection<T> : IReadOnlyCollection<T>
{
event NotifyCollectionChangedEventHandler<T> CollectionChanged;
object SyncRoot { get; }
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
}
public interface IFreezedCollection<T>
{
ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false);
ISortableSynchronizedView<T, TView> CreateSortableView<TView>(Func<T, TView> transform);
}
public interface ISynchronizedView<T, TView> : IReadOnlyCollection<(T Value, TView View)>, IDisposable
{
object SyncRoot { get; }
event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
event Action<NotifyCollectionChangedAction> CollectionStateChanged;
void AttachFilter(ISynchronizedViewFilter<T, TView> filter);
void ResetFilter(Action<T, TView> resetAction);
INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged();
}
public interface ISortableSynchronizedView<T, TView> : ISynchronizedView<T, TView>
{
void Sort(IComparer<T> comparer);
void Sort(IComparer<TView> viewComparer);
}
// will be implemented in the future?
//public interface IGroupedSynchoronizedView<T, TKey, TView> : ILookup<TKey, (T, TView)>, ISynchronizedView<T, TView>
//{
//}
public interface INotifyCollectionChangedSynchronizedView<T, TView> : ISynchronizedView<T, TView>, INotifyCollectionChanged, INotifyPropertyChanged
{
}
public static class ObservableCollectionsExtensions
{
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<T> comparer)
{
return new SortedView<T, TKey, TView>(source, identitySelector, transform, comparer);
}
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<TView> viewComparer)
{
return new SortedViewViewComparer<T, TKey, TView>(source, identitySelector, transform, viewComparer);
}
public static ISynchronizedView<T, TView> CreateSortedView<T, TKey, TView, TCompare>(this IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, Func<T, TCompare> compareSelector, bool ascending = true)
{
return source.CreateSortedView(identitySelector, transform, new AnonymousComparer<T, TCompare>(compareSelector, ascending));
}
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView>(this IFreezedCollection<T> source, Func<T, TView> transform, IComparer<T> initialSort)
{
var view = source.CreateSortableView(transform);
view.Sort(initialSort);
return view;
}
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView>(this IFreezedCollection<T> source, Func<T, TView> transform, IComparer<TView> initialViewSort)
{
var view = source.CreateSortableView(transform);
view.Sort(initialViewSort);
return view;
}
public static ISortableSynchronizedView<T, TView> CreateSortableView<T, TView, TCompare>(this IFreezedCollection<T> source, Func<T, TView> transform, Func<T, TCompare> initialCompareSelector, bool ascending = true)
{
var view = source.CreateSortableView(transform);
view.Sort(initialCompareSelector, ascending);
return view;
}
public static void Sort<T, TView, TCompare>(this ISortableSynchronizedView<T, TView> source, Func<T, TCompare> compareSelector, bool ascending = true)
{
source.Sort(new AnonymousComparer<T, TCompare>(compareSelector, ascending));
}
class AnonymousComparer<T, TCompare> : IComparer<T>
{
readonly Func<T, TCompare> selector;
readonly int f;
public AnonymousComparer(Func<T, TCompare> selector, bool ascending)
{
this.selector = selector;
this.f = ascending ? 1 : -1;
}
public int Compare(T x, T y)
{
if (x == null && y == null) return 0;
if (x == null) return 1 * f;
if (y == null) return -1 * f;
return Comparer<TCompare>.Default.Compare(selector(x), selector(y)) * f;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bdb4cc0f6a8b396418c8250eeaf2d2c8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,121 @@
using System;
namespace ObservableCollections
{
public interface ISynchronizedViewFilter<T, TView>
{
bool IsMatch(T value, TView view);
void WhenTrue(T value, TView view);
void WhenFalse(T value, TView view);
void OnCollectionChanged(ChangedKind changedKind, T value, TView view);
}
public enum ChangedKind
{
Add, Remove, Move
}
public class SynchronizedViewFilter<T, TView> : ISynchronizedViewFilter<T, TView>
{
public static readonly ISynchronizedViewFilter<T, TView> Null = new NullViewFilter();
readonly Func<T, TView, bool> isMatch;
readonly Action<T, TView> whenTrue;
readonly Action<T, TView> whenFalse;
readonly Action<ChangedKind, T, TView> onCollectionChanged;
public SynchronizedViewFilter(Func<T, TView, bool> isMatch, Action<T, TView> whenTrue, Action<T, TView> whenFalse, Action<ChangedKind, T, TView> onCollectionChanged)
{
this.isMatch = isMatch;
this.whenTrue = whenTrue;
this.whenFalse = whenFalse;
this.onCollectionChanged = onCollectionChanged;
}
public bool IsMatch(T value, TView view) => isMatch(value, view);
public void WhenFalse(T value, TView view) => whenFalse?.Invoke(value, view);
public void WhenTrue(T value, TView view) => whenTrue?.Invoke(value, view);
public void OnCollectionChanged(ChangedKind changedKind, T value, TView view) => onCollectionChanged?.Invoke(changedKind, value, view);
class NullViewFilter : ISynchronizedViewFilter<T, TView>
{
public bool IsMatch(T value, TView view) => true;
public void WhenFalse(T value, TView view) { }
public void WhenTrue(T value, TView view) { }
public void OnCollectionChanged(ChangedKind changedKind, T value, TView view) { }
}
}
public static class SynchronizedViewFilterExtensions
{
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> filter)
{
source.AttachFilter(new SynchronizedViewFilter<T, TView>(filter, null, null, null));
}
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> isMatch, Action<T, TView> whenTrue, Action<T, TView> whenFalse)
{
source.AttachFilter(new SynchronizedViewFilter<T, TView>(isMatch, whenTrue, whenFalse, null));
}
public static void AttachFilter<T, TView>(this ISynchronizedView<T, TView> source, Func<T, TView, bool> isMatch, Action<T, TView> whenTrue, Action<T, TView> whenFalse, Action<ChangedKind, T, TView> onCollectionChanged)
{
source.AttachFilter(new SynchronizedViewFilter<T, TView>(isMatch, whenTrue, whenFalse, onCollectionChanged));
}
public static bool IsNullFilter<T, TView>(this ISynchronizedViewFilter<T, TView> filter)
{
return filter == SynchronizedViewFilter<T, TView>.Null;
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value)
{
InvokeOnAdd(filter, value.value, value.view);
}
internal static void InvokeOnAdd<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view)
{
if (filter.IsMatch(value, view))
{
filter.WhenTrue(value, view);
}
else
{
filter.WhenFalse(value, view);
}
filter.OnCollectionChanged(ChangedKind.Add, value, view);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value)
{
InvokeOnRemove(filter, value.value, value.view);
}
internal static void InvokeOnRemove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view)
{
filter.OnCollectionChanged(ChangedKind.Remove, value, view);
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, (T value, TView view) value)
{
InvokeOnMove(filter, value.value, value.view);
}
internal static void InvokeOnMove<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view)
{
filter.OnCollectionChanged(ChangedKind.Move, value, view);
}
internal static void InvokeOnAttach<T, TView>(this ISynchronizedViewFilter<T, TView> filter, T value, TView view)
{
if (filter.IsMatch(value, view))
{
filter.WhenTrue(value, view);
}
else
{
filter.WhenFalse(value, view);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0ffdcef914adf8848a485ac600cc41c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0b3e01975c6d488409ce0ac753c89870
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace ObservableCollections.Internal
{
/// <summary>
/// ReadOnly cloned collection.
/// </summary>
internal struct CloneCollection<T> : IDisposable
{
T[] array;
int length;
public ReadOnlySpan<T> Span => array.AsSpan(0, length);
public IEnumerable<T> AsEnumerable() => new EnumerableCollection(array, length);
public CloneCollection(T item)
{
this.array = ArrayPool<T>.Shared.Rent(1);
this.length = 1;
}
public CloneCollection(IEnumerable<T> source)
{
if (source.TryGetNonEnumeratedCount(out var count))
{
var array = ArrayPool<T>.Shared.Rent(count);
if (source is ICollection<T> c)
{
c.CopyTo(array, 0);
}
else
{
var i = 0;
foreach (var item in source)
{
array[i++] = item;
}
}
this.array = array;
this.length = count;
}
else
{
var array = ArrayPool<T>.Shared.Rent(count);
var i = 0;
foreach (var item in source)
{
TryEnsureCapacity(ref array, i);
array[i++] = item;
}
this.array = array;
this.length = i;
}
}
public CloneCollection(ReadOnlySpan<T> source)
{
var array = ArrayPool<T>.Shared.Rent(source.Length);
source.CopyTo(array);
this.array = array;
this.length = source.Length;
}
static void TryEnsureCapacity(ref T[] array, int index)
{
if (array.Length == index)
{
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
}
array = ArrayPool<T>.Shared.Rent(index * 2);
}
public void Dispose()
{
if (array != null)
{
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
array = null!;
}
}
// Optimize to use Count and CopyTo
class EnumerableCollection : ICollection<T>
{
readonly T[] array;
readonly int count;
public EnumerableCollection(T[] array, int count)
{
if (array == null)
{
this.array = Array.Empty<T>();
this.count = 0;
}
else
{
this.array = array;
this.count = count;
}
}
public int Count => count;
public bool IsReadOnly => true;
public void Add(T item) => throw new NotSupportedException();
public void Clear() => throw new NotSupportedException();
public bool Contains(T item) => throw new NotSupportedException();
public void CopyTo(T[] dest, int destIndex) => array.CopyTo(dest, destIndex);
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < count; i++)
{
yield return array[i];
}
}
public bool Remove(T item) => throw new NotSupportedException();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b9643f0fa1bf7447961693f16cb557b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,205 @@
#pragma warning disable CS0067
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections.Internal
{
internal sealed class FreezedView<T, TView> : ISynchronizedView<T, TView>
{
readonly bool reverse;
readonly List<(T, TView)> list;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public FreezedView(IEnumerable<T> source, Func<T, TView> selector, bool reverse)
{
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.list = source.Select(x => (x, selector(x))).ToList();
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in list)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
internal sealed class FreezedSortableView<T, TView> : ISortableSynchronizedView<T, TView>
{
readonly (T, TView)[] array;
ISynchronizedViewFilter<T, TView> filter;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public object SyncRoot { get; } = new object();
public FreezedSortableView(IEnumerable<T> source, Func<T, TView> selector)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.array = source.Select(x => (x, selector(x))).ToArray();
}
public int Count
{
get
{
lock (SyncRoot)
{
return array.Length;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in array)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in array)
{
resetAction(item, view);
}
}
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, array.AsEnumerable().GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
}
public void Sort(IComparer<T> comparer)
{
Array.Sort(array, new TComparer(comparer));
}
public void Sort(IComparer<TView> viewComparer)
{
Array.Sort(array, new TViewComparer(viewComparer));
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
class TComparer : IComparer<(T, TView)>
{
readonly IComparer<T> comparer;
public TComparer(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item1, y.Item1);
}
}
class TViewComparer : IComparer<(T, TView)>
{
readonly IComparer<TView> comparer;
public TViewComparer(IComparer<TView> comparer)
{
this.comparer = comparer;
}
public int Compare((T, TView) x, (T, TView) y)
{
return comparer.Compare(x.Item2, y.Item2);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a768cdaf951b40345b6d5b21ade3e282
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
namespace ObservableCollections.Internal
{
internal class NotifyCollectionChangedSynchronizedView<T, TView> : INotifyCollectionChangedSynchronizedView<T, TView>
{
readonly ISynchronizedView<T, TView> parent;
static readonly PropertyChangedEventArgs CountPropertyChangedEventArgs = new PropertyChangedEventArgs("Count");
public NotifyCollectionChangedSynchronizedView(ISynchronizedView<T, TView> parent)
{
this.parent = parent;
this.parent.RoutingCollectionChanged += Parent_RoutingCollectionChanged;
}
private void Parent_RoutingCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
CollectionChanged?.Invoke(this, e.ToStandardEventArgs());
switch (e.Action)
{
// add, remove, reset will change the count.
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset:
PropertyChanged?.Invoke(this, CountPropertyChangedEventArgs);
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
default:
break;
}
}
public object SyncRoot => parent.SyncRoot;
public int Count => parent.Count;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged
{
add { parent.CollectionStateChanged += value; }
remove { parent.CollectionStateChanged -= value; }
}
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged
{
add { parent.RoutingCollectionChanged += value; }
remove { parent.RoutingCollectionChanged -= value; }
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter) => parent.AttachFilter(filter);
public void ResetFilter(Action<T, TView> resetAction) => parent.ResetFilter(resetAction);
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged() => this;
public void Dispose()
{
this.parent.RoutingCollectionChanged -= Parent_RoutingCollectionChanged;
parent.Dispose();
}
public IEnumerator<(T, TView)> GetEnumerator() => parent.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => parent.GetEnumerator();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0e2d983b8f809a45b60f610d0542761
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,56 @@
using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ObservableCollections.Internal
{
// internal ref struct ResizableArray<T>
internal struct ResizableArray<T> : IDisposable
{
T[] array;
int count;
public ReadOnlySpan<T> Span => array.AsSpan(0, count);
public ResizableArray(int initialCapacity)
{
array = ArrayPool<T>.Shared.Rent(initialCapacity);
count = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
if (array == null) Throw();
if (array.Length == count)
{
EnsureCapacity();
}
array[count++] = item;
}
[MethodImpl(MethodImplOptions.NoInlining)]
void EnsureCapacity()
{
var newArray = array.AsSpan().ToArray();
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
array = newArray;
}
public void Dispose()
{
if (array != null)
{
ArrayPool<T>.Shared.Return(array, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
array = null;
}
}
[DoesNotReturn]
void Throw()
{
throw new ObjectDisposedException("ResizableArray");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1477839cc1e36a4187e91e4fd001dd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,216 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections.Internal
{
internal class SortedView<T, TKey, TView> : ISynchronizedView<T, TView>
{
readonly IObservableCollection<T> source;
readonly Func<T, TView> transform;
readonly Func<T, TKey> identitySelector;
readonly SortedDictionary<(T Value, TKey Key), (T Value, TView View)> dict;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; } = new object();
public SortedView(IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<T> comparer)
{
this.source = source;
this.identitySelector = identitySelector;
this.transform = transform;
this.filter = SynchronizedViewFilter<T, TView>.Null;
lock (source.SyncRoot)
{
var dict = new SortedDictionary<(T, TKey), (T, TView)>(new Comparer(comparer));
foreach (var v in source)
{
dict.Add((v, identitySelector(v)), (v, transform(v)));
}
this.dict = dict;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return dict.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (_, (value, view)) in dict)
{
resetAction(value, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, dict.Select(x => x.Value).GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
// Add, Insert
if (e.IsSingleItem)
{
var value = e.NewItem;
var view = transform(value);
var id = identitySelector(value);
dict.Add((value, id), (value, view));
filter.InvokeOnAdd(value, view);
}
else
{
foreach (var value in e.NewItems)
{
var view = transform(value);
var id = identitySelector(value);
dict.Add((value, id), (value, view));
filter.InvokeOnAdd(value, view);
}
}
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (e.IsSingleItem)
{
var value = e.OldItem;
var id = identitySelector(value);
dict.Remove((value, id), out var v);
filter.InvokeOnRemove(v.Value, v.View);
}
else
{
foreach (var value in e.OldItems)
{
var id = identitySelector(value);
dict.Remove((value, id), out var v);
filter.InvokeOnRemove(v.Value, v.View);
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
// ReplaceRange is not supported in all ObservableCollections collections
// Replace is remove old item and insert new item.
{
var oldValue = e.OldItem;
dict.Remove((oldValue, identitySelector(oldValue)), out var oldView);
var value = e.NewItem;
var view = transform(value);
var id = identitySelector(value);
dict.Add((value, id), (value, view));
filter.InvokeOnRemove(oldView);
filter.InvokeOnAdd(value, view);
}
break;
case NotifyCollectionChangedAction.Move:
{
// Move(index change) does not affect sorted list.
var oldValue = e.OldItem;
if (dict.TryGetValue((oldValue, identitySelector(oldValue)), out var view))
{
filter.InvokeOnMove(view);
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in dict)
{
filter.InvokeOnRemove(item.Value);
}
}
dict.Clear();
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
sealed class Comparer : IComparer<(T value, TKey id)>
{
readonly IComparer<T> comparer;
public Comparer(IComparer<T> comparer)
{
this.comparer = comparer;
}
public int Compare((T value, TKey id) x, (T value, TKey id) y)
{
var compare = comparer.Compare(x.value, y.value);
if (compare == 0)
{
compare = Comparer<TKey>.Default.Compare(x.id, y.id);
}
return compare;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 768c43ec998d9754d8cc13398ed69d39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,234 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections.Internal
{
internal class SortedViewViewComparer<T, TKey, TView> : ISynchronizedView<T, TView>
{
readonly IObservableCollection<T> source;
readonly Func<T, TView> transform;
readonly Func<T, TKey> identitySelector;
readonly Dictionary<TKey, TView> viewMap; // view-map needs to use in remove.
readonly SortedDictionary<(TView View, TKey Key), (T Value, TView View)> list;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; } = new object();
public SortedViewViewComparer(IObservableCollection<T> source, Func<T, TKey> identitySelector, Func<T, TView> transform, IComparer<TView> comparer)
{
this.source = source;
this.identitySelector = identitySelector;
this.transform = transform;
this.filter = SynchronizedViewFilter<T, TView>.Null;
lock (source.SyncRoot)
{
var dict = new SortedDictionary<(TView, TKey), (T, TView)>(new Comparer(comparer));
this.viewMap = new Dictionary<TKey, TView>();
foreach (var value in source)
{
var view = transform(value);
var id = identitySelector(value);
dict.Add((view, id), (value, view));
viewMap.Add(id, view);
}
this.list = dict;
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in list)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (_, (value, view)) in list)
{
resetAction(value, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.Select(x => x.Value).GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
// Add, Insert
if (e.IsSingleItem)
{
var value = e.NewItem;
var view = transform(value);
var id = identitySelector(value);
list.Add((view, id), (value, view));
viewMap.Add(id, view);
filter.InvokeOnAdd(value, view);
}
else
{
foreach (var value in e.NewItems)
{
var view = transform(value);
var id = identitySelector(value);
list.Add((view, id), (value, view));
viewMap.Add(id, view);
filter.InvokeOnAdd(value, view);
}
}
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (e.IsSingleItem)
{
var value = e.OldItem;
var id = identitySelector(value);
if (viewMap.Remove(id, out var view))
{
list.Remove((view, id), out var v);
filter.InvokeOnRemove(v);
}
}
else
{
foreach (var value in e.OldItems)
{
var id = identitySelector(value);
if (viewMap.Remove(id, out var view))
{
list.Remove((view, id), out var v);
filter.InvokeOnRemove(v);
}
}
}
}
break;
case NotifyCollectionChangedAction.Replace:
// Replace is remove old item and insert new item.
{
var oldValue = e.OldItem;
var oldKey = identitySelector(oldValue);
if (viewMap.Remove(oldKey, out var oldView))
{
list.Remove((oldView, oldKey));
filter.InvokeOnRemove(oldValue, oldView);
}
var value = e.NewItem;
var view = transform(value);
var id = identitySelector(value);
list.Add((view, id), (value, view));
viewMap.Add(id, view);
filter.InvokeOnAdd(value, view);
}
break;
case NotifyCollectionChangedAction.Move:
// Move(index change) does not affect soreted dict.
{
var value = e.OldItem;
var key = identitySelector(value);
if (viewMap.TryGetValue(key, out var view))
{
filter.InvokeOnMove(value, view);
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in list)
{
filter.InvokeOnRemove(item.Value);
}
}
list.Clear();
viewMap.Clear();
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
sealed class Comparer : IComparer<(TView view, TKey id)>
{
readonly IComparer<TView> comparer;
public Comparer(IComparer<TView> comparer)
{
this.comparer = comparer;
}
public int Compare((TView view, TKey id) x, (TView view, TKey id) y)
{
var compare = comparer.Compare(x.view, y.view);
if (compare == 0)
{
compare = Comparer<TKey>.Default.Compare(x.id, y.id);
}
return compare;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08e8703f31991094aa9216bf7166a549
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ObservableCollections.Internal
{
internal class SynchronizedEnumerator<T> : IEnumerator<T>
{
bool isDisposed;
readonly object gate;
readonly bool lockTaken;
readonly IEnumerator<T> enumerator;
public SynchronizedEnumerator(object gate, IEnumerator<T> enumerator)
{
this.gate = gate;
this.enumerator = enumerator;
Monitor.Enter(gate, ref lockTaken);
}
public T Current => enumerator.Current;
object IEnumerator.Current => Current;
public bool MoveNext() => enumerator.MoveNext();
public void Reset() => enumerator.Reset();
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
try
{
enumerator.Dispose();
}
finally
{
if (lockTaken)
{
Monitor.Exit(gate);
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08405416bc7c7e04e9bcaa4fa13c875f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ObservableCollections.Internal
{
internal class SynchronizedViewEnumerator<T, TView> : IEnumerator<(T, TView)>, IDisposable
{
bool isDisposed;
readonly bool lockTaken;
readonly object gate;
readonly IEnumerator<(T, TView)> enumerator;
readonly ISynchronizedViewFilter<T, TView> filter;
(T, TView) current;
public SynchronizedViewEnumerator(object gate, IEnumerator<(T, TView)> enumerator, ISynchronizedViewFilter<T, TView> filter)
{
this.gate = gate;
this.enumerator = enumerator;
this.filter = filter;
this.current = default;
this.isDisposed = false;
Monitor.Enter(gate, ref lockTaken);
}
public (T, TView) Current => current;
object IEnumerator.Current => Current;
public bool MoveNext()
{
while (enumerator.MoveNext())
{
current = enumerator.Current;
if (filter.IsMatch(current.Item1, current.Item2))
{
return true;
}
}
return false;
}
public void Reset() => throw new NotSupportedException();
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
try
{
enumerator.Dispose();
}
finally
{
if (lockTaken)
{
Monitor.Exit(gate);
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98be7e821cfba26469f0911671fb24b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Specialized;
namespace ObservableCollections
{
/// <summary>
/// Contract:
/// IsSingleItem ? (NewItem, OldItem) : (NewItems, OldItems)
/// Action.Add
/// NewItem, NewItems, NewStartingIndex
/// Action.Remove
/// OldItem, OldItems, OldStartingIndex
/// Action.Replace
/// NewItem, NewItems, OldItem, OldItems, (NewStartingIndex, OldStartingIndex = samevalue)
/// Action.Move
/// NewStartingIndex, OldStartingIndex
/// Action.Reset
/// -
/// </summary>
public readonly ref struct NotifyCollectionChangedEventArgs<T>
{
public readonly NotifyCollectionChangedAction Action;
public readonly bool IsSingleItem;
public readonly T NewItem;
public readonly T OldItem;
public readonly ReadOnlySpan<T> NewItems;
public readonly ReadOnlySpan<T> OldItems;
public readonly int NewStartingIndex;
public readonly int OldStartingIndex;
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default, T oldItem = default, ReadOnlySpan<T> newItems = default, ReadOnlySpan<T> oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1)
{
Action = action;
IsSingleItem = isSingleItem;
NewItem = newItem;
OldItem = oldItem;
NewItems = newItems;
OldItems = oldItems;
NewStartingIndex = newStartingIndex;
OldStartingIndex = oldStartingIndex;
}
public NotifyCollectionChangedEventArgs ToStandardEventArgs()
{
switch (Action)
{
case NotifyCollectionChangedAction.Add:
if (IsSingleItem)
{
return new NotifyCollectionChangedEventArgs(Action, NewItem, NewStartingIndex);
}
else
{
return new NotifyCollectionChangedEventArgs(Action, NewItems.ToArray(), NewStartingIndex);
}
case NotifyCollectionChangedAction.Remove:
if (IsSingleItem)
{
return new NotifyCollectionChangedEventArgs(Action, OldItem, OldStartingIndex);
}
else
{
return new NotifyCollectionChangedEventArgs(Action, OldItems.ToArray(), OldStartingIndex);
}
case NotifyCollectionChangedAction.Replace:
if (IsSingleItem)
{
return new NotifyCollectionChangedEventArgs(Action, NewItem, OldItem, NewStartingIndex);
}
else
{
return new NotifyCollectionChangedEventArgs(Action, NewItems.ToArray(), OldItems.ToArray(), NewStartingIndex);
}
case NotifyCollectionChangedAction.Move:
{
return new NotifyCollectionChangedEventArgs(Action, OldItem, NewStartingIndex, OldStartingIndex);
}
case NotifyCollectionChangedAction.Reset:
return new NotifyCollectionChangedEventArgs(Action);
default:
throw new ArgumentOutOfRangeException();
}
}
public static NotifyCollectionChangedEventArgs<T> Add(T newItem, int newStartingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, true, newItem: newItem, newStartingIndex: newStartingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Add(ReadOnlySpan<T> newItems, int newStartingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Add, false, newItems: newItems, newStartingIndex: newStartingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Remove(T oldItem, int oldStartingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, true, oldItem: oldItem, oldStartingIndex: oldStartingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Remove(ReadOnlySpan<T> oldItems, int oldStartingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Remove, false, oldItems: oldItems, oldStartingIndex: oldStartingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Replace(T newItem, T oldItem, int startingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Replace, true, newItem: newItem, oldItem: oldItem, newStartingIndex: startingIndex, oldStartingIndex: startingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Replace(ReadOnlySpan<T> newItems, ReadOnlySpan<T> oldItems, int startingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Replace, false, newItems: newItems, oldItems: oldItems, newStartingIndex: startingIndex, oldStartingIndex: startingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Move(T changedItem, int newStartingIndex, int oldStartingIndex)
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Move, true, oldItem: changedItem, newItem: changedItem, newStartingIndex: newStartingIndex, oldStartingIndex: oldStartingIndex);
}
public static NotifyCollectionChangedEventArgs<T> Reset()
{
return new NotifyCollectionChangedEventArgs<T>(NotifyCollectionChangedAction.Reset, true);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70f96ba473572df4d91f125c2663b868
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,174 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue>
{
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, bool _ = false)
{
// reverse is no used.
return new View<TView>(this, transform);
}
// using key implicitly
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<KeyValuePair<TKey, TValue>> comparer)
{
return this.CreateSortedView(x => x.Key, transform, comparer);
}
public ISynchronizedView<KeyValuePair<TKey, TValue>, TView> CreateSortedView<TView>(Func<KeyValuePair<TKey, TValue>, TView> transform, IComparer<TView> viewComparer)
{
return this.CreateSortedView(x => x.Key, transform, viewComparer);
}
class View<TView> : ISynchronizedView<KeyValuePair<TKey, TValue>, TView>
{
readonly ObservableDictionary<TKey, TValue> source;
readonly Func<KeyValuePair<TKey, TValue>, TView> selector;
ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter;
readonly Dictionary<TKey, (TValue, TView)> dict;
public View(ObservableDictionary<TKey, TValue> source, Func<KeyValuePair<TKey, TValue>, TView> selector)
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = source.dictionary.ToDictionary(x => x.Key, x => (x.Value, selector(x)));
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public object SyncRoot { get; }
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public int Count
{
get
{
lock (SyncRoot)
{
return dict.Count;
}
}
}
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
public void AttachFilter(ISynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var v in dict)
{
filter.InvokeOnAttach(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
}
}
}
public void ResetFilter(Action<KeyValuePair<TKey, TValue>, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<KeyValuePair<TKey, TValue>, TView>.Null;
if (resetAction != null)
{
foreach (var v in dict)
{
resetAction(new KeyValuePair<TKey, TValue>(v.Key, v.Value.Item1), v.Value.Item2);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<KeyValuePair<TKey, TValue>, TView>(this);
}
}
public IEnumerator<(KeyValuePair<TKey, TValue>, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<KeyValuePair<TKey, TValue>, TView>(SyncRoot,
dict.Select(x => (new KeyValuePair<TKey, TValue>(x.Key, x.Value.Item1), x.Value.Item2)).GetEnumerator(),
filter);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>> e)
{
// ObservableDictionary only provides single item operation and does not use int index.
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
var v = selector(e.NewItem);
dict.Add(e.NewItem.Key, (e.NewItem.Value, v));
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
}
break;
case NotifyCollectionChangedAction.Remove:
{
if (dict.Remove(e.OldItem.Key, out var v))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, v.Item1), v.Item2));
}
}
break;
case NotifyCollectionChangedAction.Replace:
{
if (dict.Remove(e.OldItem.Key, out var oldView))
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(e.OldItem.Key, oldView.Item1), oldView.Item2));
}
var v = selector(e.NewItem);
dict[e.NewItem.Key] = (e.NewItem.Value, v);
filter.InvokeOnAdd(new KeyValuePair<TKey, TValue>(e.NewItem.Key, e.NewItem.Value), v);
}
break;
case NotifyCollectionChangedAction.Reset:
{
if (!filter.IsNullFilter())
{
foreach (var item in dict)
{
filter.InvokeOnRemove((new KeyValuePair<TKey, TValue>(item.Key, item.Value.Item1), item.Value.Item2));
}
}
dict.Clear();
}
break;
case NotifyCollectionChangedAction.Move: // ObservableDictionary have no Move operation.
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f512d4ba320de58428976eb614bfa06d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,222 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
public sealed partial class ObservableDictionary<TKey, TValue>
: IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, IObservableCollection<KeyValuePair<TKey, TValue>>
{
readonly Dictionary<TKey, TValue> dictionary;
public object SyncRoot { get; } = new object();
public ObservableDictionary()
{
this.dictionary = new Dictionary<TKey, TValue>();
}
public ObservableDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection)
{
#if NET6_0_OR_GREATER
this.dictionary = new Dictionary<TKey, TValue>(collection);
#else
this.dictionary = new Dictionary<TKey, TValue>();
foreach (var item in collection)
{
dictionary.Add(item.Key, item.Value);
}
#endif
}
public event NotifyCollectionChangedEventHandler<KeyValuePair<TKey, TValue>> CollectionChanged;
public TValue this[TKey key]
{
get
{
lock (SyncRoot)
{
return dictionary[key];
}
}
set
{
lock (SyncRoot)
{
if (dictionary.TryGetValue(key, out var oldValue))
{
dictionary[key] = value;
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Replace(
new KeyValuePair<TKey, TValue>(key, value),
new KeyValuePair<TKey, TValue>(key, oldValue),
-1));
}
else
{
Add(key, value);
}
}
}
}
// for lock synchronization, hide keys and values.
ICollection<TKey> IDictionary<TKey, TValue>.Keys
{
get
{
lock (SyncRoot)
{
return dictionary.Keys;
}
}
}
ICollection<TValue> IDictionary<TKey, TValue>.Values
{
get
{
lock (SyncRoot)
{
return dictionary.Values;
}
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return dictionary.Count;
}
}
}
public bool IsReadOnly => false;
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
{
get
{
lock (SyncRoot)
{
return dictionary.Keys;
}
}
}
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
{
get
{
lock (SyncRoot)
{
return dictionary.Values;
}
}
}
public void Add(TKey key, TValue value)
{
lock (SyncRoot)
{
dictionary.Add(key, value);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Add(new KeyValuePair<TKey, TValue>(key, value), -1));
}
}
public void Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
lock (SyncRoot)
{
dictionary.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Reset());
}
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
lock (SyncRoot)
{
return ((ICollection<KeyValuePair<TKey, TValue>>)dictionary).Contains(item);
}
}
public bool ContainsKey(TKey key)
{
lock (SyncRoot)
{
return ((IDictionary<TKey, TValue>)dictionary).ContainsKey(key);
}
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
lock (SyncRoot)
{
((ICollection<KeyValuePair<TKey, TValue>>)dictionary).CopyTo(array, arrayIndex);
}
}
public bool Remove(TKey key)
{
lock (SyncRoot)
{
if (dictionary.Remove(key, out var value))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(key, value), -1));
return true;
}
return false;
}
}
public bool Remove(KeyValuePair<TKey, TValue> item)
{
lock (SyncRoot)
{
if (dictionary.TryGetValue(item.Key, out var value))
{
if (EqualityComparer<TValue>.Default.Equals(value, item.Value))
{
if (dictionary.Remove(item.Key, out var value2))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<KeyValuePair<TKey, TValue>>.Remove(new KeyValuePair<TKey, TValue>(item.Key, value2), -1));
return true;
}
}
}
return false;
}
}
#pragma warning disable CS8767
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
#pragma warning restore CS8767
{
lock (SyncRoot)
{
return dictionary.TryGetValue(key, out value);
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return new SynchronizedEnumerator<KeyValuePair<TKey, TValue>>(SyncRoot, dictionary.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b4dc0886473cf8c49947c9081454c614
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,296 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ObservableCollections
{
public sealed partial class ObservableFixedSizeRingBuffer<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
{
readonly RingBuffer<T> buffer;
readonly int capacity;
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public ObservableFixedSizeRingBuffer(int capacity)
{
this.capacity = capacity;
this.buffer = new RingBuffer<T>(capacity);
}
public ObservableFixedSizeRingBuffer(int capacity, IEnumerable<T> collection)
{
this.capacity = capacity;
this.buffer = new RingBuffer<T>(capacity);
foreach (var item in collection)
{
if (capacity == buffer.Count)
{
buffer.RemoveFirst();
}
buffer.AddLast(item);
}
}
public bool IsReadOnly => false;
public object SyncRoot { get; } = new object();
public T this[int index]
{
get
{
lock (SyncRoot)
{
return this.buffer[index];
}
}
set
{
lock (SyncRoot)
{
var oldValue = buffer[index];
buffer[index] = value;
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Replace(value, oldValue, index));
}
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return buffer.Count;
}
}
}
public void AddFirst(T item)
{
lock (SyncRoot)
{
if (capacity == buffer.Count)
{
var remItem = buffer.RemoveLast();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(remItem, capacity - 1));
}
buffer.AddFirst(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, 0));
}
}
public void AddLast(T item)
{
lock (SyncRoot)
{
if (capacity == buffer.Count)
{
var remItem = buffer.RemoveFirst();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(remItem, 0));
}
buffer.AddLast(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, buffer.Count - 1));
}
}
public T RemoveFirst()
{
lock (SyncRoot)
{
var item = buffer.RemoveFirst();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, 0));
return item;
}
}
public T RemoveLast()
{
lock (SyncRoot)
{
var index = buffer.Count - 1;
var item = buffer.RemoveLast();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, index));
return item;
}
}
// AddFirstRange is not exists.
public void AddLastRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
using (var xs = new CloneCollection<T>(items))
{
if (capacity <= buffer.Count + xs.Span.Length)
{
// calc remove count
var remCount = Math.Min(buffer.Count, buffer.Count + xs.Span.Length - capacity);
using (var ys = new ResizableArray<T>(remCount))
{
for (int i = 0; i < remCount; i++)
{
ys.Add(buffer.RemoveFirst());
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
}
}
var index = buffer.Count;
var span = xs.Span;
if (span.Length > capacity)
{
span = span.Slice(span.Length - capacity);
}
foreach (var item in span)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
}
}
}
public void AddLastRange(T[] items)
{
lock (SyncRoot)
{
if (capacity <= buffer.Count + items.Length)
{
// calc remove count
var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity);
using (var ys = new ResizableArray<T>(remCount))
{
for (int i = 0; i < remCount; i++)
{
ys.Add(buffer.RemoveFirst());
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
}
}
var index = buffer.Count;
var span = items.AsSpan();
if (span.Length > capacity)
{
span = span.Slice(span.Length - capacity);
}
foreach (var item in span)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
}
}
public void AddLastRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
if (capacity <= buffer.Count + items.Length)
{
// calc remove count
var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity);
using (var ys = new ResizableArray<T>(remCount))
{
for (int i = 0; i < remCount; i++)
{
ys.Add(buffer.RemoveFirst());
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
}
}
var index = buffer.Count;
var span = items;
if (span.Length > capacity)
{
span = span.Slice(span.Length - capacity);
}
foreach (var item in span)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
}
}
public int IndexOf(T item)
{
lock (SyncRoot)
{
return buffer.IndexOf(item);
}
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
void ICollection<T>.Add(T item)
{
AddLast(item);
}
public void Clear()
{
lock (SyncRoot)
{
buffer.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
public bool Contains(T item)
{
lock (SyncRoot)
{
return buffer.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (SyncRoot)
{
buffer.CopyTo(array, arrayIndex);
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, buffer.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new ObservableRingBuffer<T>.View<TView>(this, transform, reverse);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 81d799cc7eb02954d89c7ee52b7c9148
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,165 @@
using ObservableCollections.Internal;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableHashSet<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool _ = false)
{
return new View<TView>(this, transform);
}
sealed class View<TView> : ISynchronizedView<T, TView>
{
readonly ObservableHashSet<T> source;
readonly Func<T, TView> selector;
readonly Dictionary<T, (T, TView)> dict;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; }
public View(ObservableHashSet<T> source, Func<T, TView> selector)
{
this.source = source;
this.selector = selector;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.dict = source.set.ToDictionary(x => x, x => (x, selector(x)));
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return dict.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (_, (value, view)) in dict)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (_, (value, view)) in dict)
{
resetAction(value, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, dict.Select(x => x.Value).GetEnumerator(), filter);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
dict.Add(e.NewItem, v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
dict.Add(item, v);
filter.InvokeOnAdd(v);
}
}
break;
case NotifyCollectionChangedAction.Remove:
if (e.IsSingleItem)
{
if (dict.Remove(e.OldItem, out var value))
{
filter.InvokeOnRemove(value.Item1, value.Item2);
}
}
else
{
foreach (var item in e.OldItems)
{
if (dict.Remove(item, out var value))
{
filter.InvokeOnRemove(value.Item1, value.Item2);
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in dict)
{
filter.InvokeOnRemove(item.Value);
}
}
dict.Clear();
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7b9e5ab06e68b242a4a722866654fd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,258 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
// can not implements ISet<T> because set operation can not get added/removed values.
public sealed partial class ObservableHashSet<T> : IReadOnlySet<T>, IReadOnlyCollection<T>, IObservableCollection<T>
{
readonly HashSet<T> set;
public object SyncRoot { get; } = new object();
public ObservableHashSet()
{
this.set = new HashSet<T>();
}
#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER
public ObservableHashSet(int capacity)
{
this.set = new HashSet<T>(capacity);
}
#endif
public ObservableHashSet(IEnumerable<T> collection)
{
this.set = new HashSet<T>(collection);
}
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public int Count
{
get
{
lock (SyncRoot)
{
return set.Count;
}
}
}
public bool IsReadOnly => false;
public bool Add(T item)
{
lock (SyncRoot)
{
if (set.Add(item))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, -1));
return true;
}
return false;
}
}
public void AddRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
if (!items.TryGetNonEnumeratedCount(out var capacity))
{
capacity = 4;
}
using (var list = new ResizableArray<T>(capacity))
{
foreach (var item in items)
{
if (set.Add(item))
{
list.Add(item);
}
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(list.Span, -1));
}
}
}
public void AddRange(T[] items)
{
AddRange(items.AsSpan());
}
public void AddRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
using (var list = new ResizableArray<T>(items.Length))
{
foreach (var item in items)
{
if (set.Add(item))
{
list.Add(item);
}
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(list.Span, -1));
}
}
}
public bool Remove(T item)
{
lock (SyncRoot)
{
if (set.Remove(item))
{
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, -1));
return true;
}
return false;
}
}
public void RemoveRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
if (!items.TryGetNonEnumeratedCount(out var capacity))
{
capacity = 4;
}
using (var list = new ResizableArray<T>(capacity))
{
foreach (var item in items)
{
if (set.Remove(item))
{
list.Add(item);
}
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(list.Span, -1));
}
}
}
public void RemoveRange(T[] items)
{
RemoveRange(items.AsSpan());
}
public void RemoveRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
using (var list = new ResizableArray<T>(items.Length))
{
foreach (var item in items)
{
if (set.Remove(item))
{
list.Add(item);
}
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(list.Span, -1));
}
}
}
public void Clear()
{
lock (SyncRoot)
{
set.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
#if !NETSTANDARD2_0 && !NET_STANDARD_2_0 && !NET_4_6
public bool TryGetValue(T equalValue, [MaybeNullWhen(false)] out T actualValue)
{
return set.TryGetValue(equalValue, out actualValue);
}
#endif
public bool Contains(T item)
{
lock (SyncRoot)
{
return set.Contains(item);
}
}
public bool IsProperSubsetOf(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.IsProperSubsetOf(other);
}
}
public bool IsProperSupersetOf(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.IsProperSupersetOf(other);
}
}
public bool IsSubsetOf(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.IsSubsetOf(other);
}
}
public bool IsSupersetOf(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.IsSupersetOf(other);
}
}
public bool Overlaps(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.Overlaps(other);
}
}
public bool SetEquals(IEnumerable<T> other)
{
lock (SyncRoot)
{
return set.SetEquals(other);
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, set.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38d982a9907bb0047b158794a1cf35e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,220 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableList<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new View<TView>(this, transform, reverse);
}
sealed class View<TView> : ISynchronizedView<T, TView>
{
readonly ObservableList<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
readonly List<(T, TView)> list;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; }
public View(ObservableList<T> source, Func<T, TView> selector, bool reverse)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.list = source.list.Select(x => (x, selector(x))).ToList();
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in list)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in list)
{
resetAction(item, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, list.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// Add
if (e.NewStartingIndex == list.Count)
{
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
list.Add(v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
list.Add(v);
filter.InvokeOnAdd(v);
}
}
}
// Insert
else
{
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
list.Insert(e.NewStartingIndex, v);
filter.InvokeOnAdd(v);
}
else
{
// inefficient copy, need refactoring
var newArray = new (T, TView)[e.NewItems.Length];
var span = e.NewItems;
for (int i = 0; i < span.Length; i++)
{
var v = (span[i], selector(span[i]));
newArray[i] = v;
filter.InvokeOnAdd(v);
}
list.InsertRange(e.NewStartingIndex, newArray);
}
}
break;
case NotifyCollectionChangedAction.Remove:
if (e.IsSingleItem)
{
var v = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
filter.InvokeOnRemove(v.Item1, v.Item2);
}
else
{
var len = e.OldStartingIndex + e.OldItems.Length;
for (int i = e.OldStartingIndex; i < len; i++)
{
var v = list[i];
filter.InvokeOnRemove(v.Item1, v.Item2);
}
list.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
}
break;
case NotifyCollectionChangedAction.Replace:
// ObservableList does not support replace range
{
var v = (e.NewItem, selector(e.NewItem));
var oldItem = list[e.NewStartingIndex];
list[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem);
filter.InvokeOnAdd(v);
break;
}
case NotifyCollectionChangedAction.Move:
{
var removeItem = list[e.OldStartingIndex];
list.RemoveAt(e.OldStartingIndex);
list.Insert(e.NewStartingIndex, removeItem);
filter.InvokeOnMove(removeItem);
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in list)
{
filter.InvokeOnRemove(item);
}
}
list.Clear();
break;
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b98cf9d4d5426ad478ede70c5ac637d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,270 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace ObservableCollections
{
public sealed partial class ObservableList<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
{
readonly List<T> list;
public object SyncRoot { get; } = new object();
public ObservableList()
{
list = new List<T>();
}
public ObservableList(int capacity)
{
list = new List<T>(capacity);
}
public ObservableList(IEnumerable<T> collection)
{
list = collection.ToList();
}
public T this[int index]
{
get
{
lock (SyncRoot)
{
return list[index];
}
}
set
{
lock (SyncRoot)
{
var oldValue = list[index];
list[index] = value;
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Replace(value, oldValue, index));
}
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return list.Count;
}
}
}
public bool IsReadOnly => false;
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public void Add(T item)
{
lock (SyncRoot)
{
var index = list.Count;
list.Add(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, index));
}
}
public void AddRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
var index = list.Count;
using (var xs = new CloneCollection<T>(items))
{
// to avoid iterate twice, require copy before insert.
list.AddRange(xs.AsEnumerable());
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
}
}
}
public void AddRange(T[] items)
{
lock (SyncRoot)
{
var index = list.Count;
list.AddRange(items);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public void AddRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
var index = list.Count;
foreach (var item in items)
{
list.Add(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public void Clear()
{
var l = new List<int>();
lock (SyncRoot)
{
list.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
public bool Contains(T item)
{
lock (SyncRoot)
{
return list.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (SyncRoot)
{
list.CopyTo(array, arrayIndex);
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, list.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void ForEach(Action<T> action)
{
lock (SyncRoot)
{
foreach (var item in list)
{
action(item);
}
}
}
public int IndexOf(T item)
{
lock (SyncRoot)
{
return list.IndexOf(item);
}
}
public void Insert(int index, T item)
{
lock (SyncRoot)
{
list.Insert(index, item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, index));
}
}
public void InsertRange(int index, T[] items)
{
lock (SyncRoot)
{
list.InsertRange(index, items);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public void InsertRange(int index, IEnumerable<T> items)
{
lock (SyncRoot)
{
using (var xs = new CloneCollection<T>(items))
{
list.InsertRange(index, xs.AsEnumerable());
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
}
}
}
public void InsertRange(int index, ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
using (var xs = new CloneCollection<T>(items))
{
list.InsertRange(index, xs.AsEnumerable());
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
}
}
}
public bool Remove(T item)
{
lock (SyncRoot)
{
var index = list.IndexOf(item);
if (index >= 0)
{
list.RemoveAt(index);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, index));
return true;
}
else
{
return false;
}
}
}
public void RemoveAt(int index)
{
lock (SyncRoot)
{
var item = list[index];
list.RemoveAt(index);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, index));
}
}
public void RemoveRange(int index, int count)
{
lock (SyncRoot)
{
#if NET5_0_OR_GREATER
var range = CollectionsMarshal.AsSpan(list).Slice(index, count);
#else
var range = list.GetRange(index, count);
#endif
// require copy before remove
using (var xs = new CloneCollection<T>(range))
{
list.RemoveRange(index, count);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(xs.Span, index));
}
}
}
public void Move(int oldIndex, int newIndex)
{
lock (SyncRoot)
{
var removedItem = list[oldIndex];
list.RemoveAt(oldIndex);
list.Insert(newIndex, removedItem);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Move(removedItem, newIndex, oldIndex));
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 963681d5debafe6448e09cd9a4029cec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,173 @@
using ObservableCollections.Internal;
using System.Collections;
using System.Collections.Specialized;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableQueue<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new View<TView>(this, transform, reverse);
}
class View<TView> : ISynchronizedView<T, TView>
{
readonly ObservableQueue<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
protected readonly Queue<(T, TView)> queue;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; }
public View(ObservableQueue<T> source, Func<T, TView> selector, bool reverse)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.queue = new Queue<(T, TView)>(source.queue.Select(x => (x, selector(x))));
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return queue.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in queue)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in queue)
{
resetAction(item, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, queue.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, queue.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// Add(Enqueue, EnqueueRange)
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
queue.Enqueue(v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
queue.Enqueue(v);
filter.InvokeOnAdd(v);
}
}
break;
case NotifyCollectionChangedAction.Remove:
// Dequeue, DequeuRange
if (e.IsSingleItem)
{
var v = queue.Dequeue();
filter.InvokeOnRemove(v.Item1, v.Item2);
}
else
{
var len = e.OldItems.Length;
for (int i = 0; i < len; i++)
{
var v = queue.Dequeue();
filter.InvokeOnRemove(v.Item1, v.Item2);
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in queue)
{
filter.InvokeOnRemove(item);
}
}
queue.Clear();
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fea76167e2a69749931bf01b5b5ed92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,214 @@
using ObservableCollections.Internal;
using System.Buffers;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableQueue<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
readonly Queue<T> queue;
public object SyncRoot { get; } = new object();
public ObservableQueue()
{
this.queue = new Queue<T>();
}
public ObservableQueue(int capacity)
{
this.queue = new Queue<T>(capacity);
}
public ObservableQueue(IEnumerable<T> collection)
{
this.queue = new Queue<T>(collection);
}
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public int Count
{
get
{
lock (SyncRoot)
{
return queue.Count;
}
}
}
public void Enqueue(T item)
{
lock (SyncRoot)
{
var index = queue.Count;
queue.Enqueue(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, index));
}
}
public void EnqueueRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
var index = queue.Count;
using (var xs = new CloneCollection<T>(items))
{
foreach (var item in xs.Span)
{
queue.Enqueue(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
}
}
}
public void EnqueueRange(T[] items)
{
lock (SyncRoot)
{
var index = queue.Count;
foreach (var item in items)
{
queue.Enqueue(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public void EnqueueRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
var index = queue.Count;
foreach (var item in items)
{
queue.Enqueue(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public T Dequeue()
{
lock (SyncRoot)
{
var v = queue.Dequeue();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(v, 0));
return v;
}
}
public bool TryDequeue([MaybeNullWhen(false)] out T result)
{
lock (SyncRoot)
{
if (queue.Count != 0)
{
result = queue.Dequeue();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(result, 0));
return true;
}
result = default;
return false;
}
}
public void DequeueRange(int count)
{
lock (SyncRoot)
{
var dest = ArrayPool<T>.Shared.Rent(count);
try
{
for (int i = 0; i < count; i++)
{
dest[i] = queue.Dequeue();
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(dest.AsSpan(0, count), 0));
}
finally
{
ArrayPool<T>.Shared.Return(dest, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
}
}
}
public void DequeueRange(Span<T> dest)
{
lock (SyncRoot)
{
for (int i = 0; i < dest.Length; i++)
{
dest[i] = queue.Dequeue();
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(dest, 0));
}
}
public void Clear()
{
lock (SyncRoot)
{
queue.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
public T Peek()
{
lock (SyncRoot)
{
return queue.Peek();
}
}
public bool TryPeek([MaybeNullWhen(false)] T result)
{
lock (SyncRoot)
{
if (queue.Count != 0)
{
result = queue.Peek();
return true;
}
result = default;
return false;
}
}
public T[] ToArray()
{
lock (SyncRoot)
{
return queue.ToArray();
}
}
public void TrimExcess()
{
lock (SyncRoot)
{
queue.TrimExcess();
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, queue.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb4558e36aaf5c84597551bb9a0d42e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,231 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableRingBuffer<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new View<TView>(this, transform, reverse);
}
// used with ObservableFixedSizeRingBuffer
internal sealed class View<TView> : ISynchronizedView<T, TView>
{
readonly IObservableCollection<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
readonly RingBuffer<(T, TView)> ringBuffer;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; }
public View(IObservableCollection<T> source, Func<T, TView> selector, bool reverse)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x))));
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return ringBuffer.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in ringBuffer)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in ringBuffer)
{
resetAction(item, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, ringBuffer.AsEnumerable().GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, ringBuffer.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// can not distinguish AddFirst and AddLast when collection count is 0.
// So, in that case, use AddLast.
// The internal structure may be different from the parent, but the result is same.
// RangeOperation is only exists AddLastRange because we can not distinguish FirstRange or LastRange.
if (e.NewStartingIndex == 0 && ringBuffer.Count != 0)
{
// AddFirst
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddFirst(v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
ringBuffer.AddFirst(v);
filter.InvokeOnAdd(v);
}
}
}
else
{
// AddLast
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
ringBuffer.AddLast(v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
ringBuffer.AddLast(v);
filter.InvokeOnAdd(v);
}
}
}
break;
case NotifyCollectionChangedAction.Remove:
// starting from 0 is RemoveFirst
if (e.OldStartingIndex == 0)
{
// RemoveFirst
if (e.IsSingleItem)
{
var v = ringBuffer.RemoveFirst();
filter.InvokeOnRemove(v);
}
else
{
for (int i = 0; i < e.OldItems.Length; i++)
{
var v = ringBuffer.RemoveFirst();
filter.InvokeOnRemove(v);
}
}
}
else
{
// RemoveLast
if (e.IsSingleItem)
{
var v = ringBuffer.RemoveLast();
filter.InvokeOnRemove(v);
}
else
{
for (int i = 0; i < e.OldItems.Length; i++)
{
var v = ringBuffer.RemoveLast();
filter.InvokeOnRemove(v);
}
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in ringBuffer)
{
filter.InvokeOnRemove(item);
}
}
ringBuffer.Clear();
break;
case NotifyCollectionChangedAction.Replace:
// range is not supported
{
var v = (e.NewItem, selector(e.NewItem));
var oldItem = ringBuffer[e.NewStartingIndex];
ringBuffer[e.NewStartingIndex] = v;
filter.InvokeOnRemove(oldItem);
filter.InvokeOnAdd(v);
break;
}
case NotifyCollectionChangedAction.Move:
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9a4b3308e0d0c4c4bafd65b41e7d66cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,206 @@
using ObservableCollections.Internal;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
namespace ObservableCollections
{
public sealed partial class ObservableRingBuffer<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
{
readonly RingBuffer<T> buffer;
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public ObservableRingBuffer()
{
this.buffer = new RingBuffer<T>();
}
public ObservableRingBuffer(IEnumerable<T> collection)
{
this.buffer = new RingBuffer<T>(collection);
}
public bool IsReadOnly => false;
public object SyncRoot { get; } = new object();
public T this[int index]
{
get
{
lock (SyncRoot)
{
return this.buffer[index];
}
}
set
{
lock (SyncRoot)
{
var oldValue = buffer[index];
buffer[index] = value;
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Replace(value, oldValue, index));
}
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return buffer.Count;
}
}
}
public void AddFirst(T item)
{
lock (SyncRoot)
{
buffer.AddFirst(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, 0));
}
}
public void AddLast(T item)
{
lock (SyncRoot)
{
buffer.AddLast(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, buffer.Count - 1));
}
}
public T RemoveFirst()
{
lock (SyncRoot)
{
var item = buffer.RemoveFirst();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, 0));
return item;
}
}
public T RemoveLast()
{
lock (SyncRoot)
{
var index = buffer.Count - 1;
var item = buffer.RemoveLast();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, index));
return item;
}
}
// AddFirstRange is not exists.
public void AddLastRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
var index = buffer.Count;
using (var xs = new CloneCollection<T>(items))
{
foreach (var item in xs.Span)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
}
}
}
public void AddLastRange(T[] items)
{
lock (SyncRoot)
{
var index = buffer.Count;
foreach (var item in items)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public void AddLastRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
var index = buffer.Count;
foreach (var item in items)
{
buffer.AddLast(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
}
}
public int IndexOf(T item)
{
lock (SyncRoot)
{
return buffer.IndexOf(item);
}
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
void ICollection<T>.Add(T item)
{
AddLast(item);
}
public void Clear()
{
lock (SyncRoot)
{
buffer.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
public bool Contains(T item)
{
lock (SyncRoot)
{
return buffer.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex)
{
lock (SyncRoot)
{
buffer.CopyTo(array, arrayIndex);
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, buffer.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c858015a9e82dc84487b3e627916a7ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,173 @@
using ObservableCollections.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ObservableCollections
{
public sealed partial class ObservableStack<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
{
return new View<TView>(this, transform, reverse);
}
class View<TView> : ISynchronizedView<T, TView>
{
readonly ObservableStack<T> source;
readonly Func<T, TView> selector;
readonly bool reverse;
protected readonly Stack<(T, TView)> stack;
ISynchronizedViewFilter<T, TView> filter;
public event NotifyCollectionChangedEventHandler<T> RoutingCollectionChanged;
public event Action<NotifyCollectionChangedAction> CollectionStateChanged;
public object SyncRoot { get; }
public View(ObservableStack<T> source, Func<T, TView> selector, bool reverse)
{
this.source = source;
this.selector = selector;
this.reverse = reverse;
this.filter = SynchronizedViewFilter<T, TView>.Null;
this.SyncRoot = new object();
lock (source.SyncRoot)
{
this.stack = new Stack<(T, TView)>(source.stack.Select(x => (x, selector(x))));
this.source.CollectionChanged += SourceCollectionChanged;
}
}
public int Count
{
get
{
lock (SyncRoot)
{
return stack.Count;
}
}
}
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
{
lock (SyncRoot)
{
this.filter = filter;
foreach (var (value, view) in stack)
{
filter.InvokeOnAttach(value, view);
}
}
}
public void ResetFilter(Action<T, TView> resetAction)
{
lock (SyncRoot)
{
this.filter = SynchronizedViewFilter<T, TView>.Null;
if (resetAction != null)
{
foreach (var (item, view) in stack)
{
resetAction(item, view);
}
}
}
}
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
{
lock (SyncRoot)
{
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
}
}
public IEnumerator<(T, TView)> GetEnumerator()
{
if (!reverse)
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, stack.GetEnumerator(), filter);
}
else
{
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, stack.AsEnumerable().Reverse().GetEnumerator(), filter);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Dispose()
{
this.source.CollectionChanged -= SourceCollectionChanged;
}
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
{
lock (SyncRoot)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// Add(Push, PushRange)
if (e.IsSingleItem)
{
var v = (e.NewItem, selector(e.NewItem));
stack.Push(v);
filter.InvokeOnAdd(v);
}
else
{
foreach (var item in e.NewItems)
{
var v = (item, selector(item));
stack.Push(v);
filter.InvokeOnAdd(v);
}
}
break;
case NotifyCollectionChangedAction.Remove:
// Pop, PopRange
if (e.IsSingleItem)
{
var v = stack.Pop();
filter.InvokeOnRemove(v.Item1, v.Item2);
}
else
{
var len = e.OldItems.Length;
for (int i = 0; i < len; i++)
{
var v = stack.Pop();
filter.InvokeOnRemove(v.Item1, v.Item2);
}
}
break;
case NotifyCollectionChangedAction.Reset:
if (!filter.IsNullFilter())
{
foreach (var item in stack)
{
filter.InvokeOnRemove(item);
}
}
stack.Clear();
break;
case NotifyCollectionChangedAction.Replace:
case NotifyCollectionChangedAction.Move:
default:
break;
}
RoutingCollectionChanged?.Invoke(e);
CollectionStateChanged?.Invoke(e.Action);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bfc46ddd0f9e1e246931d87579f595b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,210 @@
using ObservableCollections.Internal;
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ObservableCollections
{
public sealed partial class ObservableStack<T> : IReadOnlyCollection<T>, IObservableCollection<T>
{
readonly Stack<T> stack;
public object SyncRoot { get; } = new object();
public ObservableStack()
{
this.stack = new Stack<T>();
}
public ObservableStack(int capacity)
{
this.stack = new Stack<T>(capacity);
}
public ObservableStack(IEnumerable<T> collection)
{
this.stack = new Stack<T>(collection);
}
public event NotifyCollectionChangedEventHandler<T> CollectionChanged;
public int Count
{
get
{
lock (SyncRoot)
{
return stack.Count;
}
}
}
public void Push(T item)
{
lock (SyncRoot)
{
var index = stack.Count;
stack.Push(item);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, index));
}
}
public void PushRange(IEnumerable<T> items)
{
lock (SyncRoot)
{
using (var xs = new CloneCollection<T>(items))
{
foreach (var item in xs.Span)
{
stack.Push(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, 0));
}
}
}
public void PushRange(T[] items)
{
lock (SyncRoot)
{
foreach (var item in items)
{
stack.Push(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, 0));
}
}
public void PushRange(ReadOnlySpan<T> items)
{
lock (SyncRoot)
{
foreach (var item in items)
{
stack.Push(item);
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, 0));
}
}
public T Pop()
{
lock (SyncRoot)
{
var v = stack.Pop();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(v, 0));
return v;
}
}
public bool TryPop([MaybeNullWhen(false)] out T result)
{
lock (SyncRoot)
{
if (stack.Count != 0)
{
result = stack.Pop();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(result, 0));
return true;
}
result = default;
return false;
}
}
public void PopRange(int count)
{
lock (SyncRoot)
{
var dest = ArrayPool<T>.Shared.Rent(count);
try
{
for (int i = 0; i < count; i++)
{
dest[i] = stack.Pop();
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(dest.AsSpan(0, count), 0));
}
finally
{
ArrayPool<T>.Shared.Return(dest, RuntimeHelpersEx.IsReferenceOrContainsReferences<T>());
}
}
}
public void PopRange(Span<T> dest)
{
lock (SyncRoot)
{
for (int i = 0; i < dest.Length; i++)
{
dest[i] = stack.Pop();
}
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(dest, 0));
}
}
public void Clear()
{
lock (SyncRoot)
{
stack.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
}
public T Peek()
{
lock (SyncRoot)
{
return stack.Peek();
}
}
public bool TryPeek([MaybeNullWhen(false)] T result)
{
lock (SyncRoot)
{
if (stack.Count != 0)
{
result = stack.Peek();
return true;
}
result = default;
return false;
}
}
public T[] ToArray()
{
lock (SyncRoot)
{
return stack.ToArray();
}
}
public void TrimExcess()
{
lock (SyncRoot)
{
stack.TrimExcess();
}
}
public IEnumerator<T> GetEnumerator()
{
return new SynchronizedEnumerator<T>(SyncRoot, stack.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e89128583a1ffee4babe861213e14be2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,368 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ObservableCollections
{
public sealed class RingBuffer<T> : IList<T>, IReadOnlyList<T>
{
T[] buffer;
int head;
int count;
int mask;
public RingBuffer()
{
this.buffer = new T[8];
this.head = 0;
this.count = 0;
this.mask = buffer.Length - 1;
}
public RingBuffer(int capacity)
{
this.buffer = new T[CalculateCapacity(capacity)];
this.head = 0;
this.count = 0;
this.mask = buffer.Length - 1;
}
public RingBuffer(IEnumerable<T> collection)
{
var array = collection.TryGetNonEnumeratedCount(out var count)
? new T[CalculateCapacity(count)]
: new T[8];
var i = 0;
foreach (var item in collection)
{
if (i == array.Length)
{
Array.Resize(ref array, i * 2);
}
array[i++] = item;
}
this.buffer = array;
this.head = 0;
this.count = i;
this.mask = buffer.Length - 1;
}
static int CalculateCapacity(int size)
{
size--;
size |= size >> 1;
size |= size >> 2;
size |= size >> 4;
size |= size >> 8;
size |= size >> 16;
size += 1;
if (size < 8)
{
size = 8;
}
return size;
}
public T this[int index]
{
get
{
var i = (head + index) & mask;
return buffer[i];
}
set
{
var i = (head + index) & mask;
buffer[i] = value;
}
}
public int Count => count;
public bool IsReadOnly => false;
public void AddLast(T item)
{
if (count == buffer.Length) EnsureCapacity();
var index = (head + count) & mask;
buffer[index] = item;
count++;
}
public void AddFirst(T item)
{
if (count == buffer.Length) EnsureCapacity();
head = (head - 1) & mask;
buffer[head] = item;
count++;
}
public T RemoveLast()
{
if (count == 0) ThrowForEmpty();
var index = (head + count - 1) & mask;
var v = buffer[index];
buffer[index] = default;
count--;
return v;
}
public T RemoveFirst()
{
if (count == 0) ThrowForEmpty();
var index = head & mask;
var v = buffer[index];
buffer[index] = default;
head = head + 1;
count--;
return v;
}
void EnsureCapacity()
{
var newBuffer = new T[buffer.Length * 2];
var i = head & mask;
buffer.AsSpan(i).CopyTo(newBuffer);
if (i != 0)
{
buffer.AsSpan(0, i).CopyTo(newBuffer.AsSpan(buffer.Length - i));
}
head = 0;
buffer = newBuffer;
mask = newBuffer.Length - 1;
}
void ICollection<T>.Add(T item)
{
AddLast(item);
}
public void Clear()
{
Array.Clear(buffer, 0, buffer.Length);
head = 0;
count = 0;
}
public RingBufferSpan<T> GetSpan()
{
var start = head & mask;
var end = (head + count) & mask;
if (end > start)
{
var first = buffer.AsSpan(start, count);
var second = Array.Empty<T>().AsSpan();
return new RingBufferSpan<T>(first, second, count);
}
else
{
var first = buffer.AsSpan(start, buffer.Length - start);
var second = buffer.AsSpan(0, end);
return new RingBufferSpan<T>(first, second, count);
}
}
public IEnumerator<T> GetEnumerator()
{
if (count == 0) yield break;
var start = head & mask;
var end = (head + count) & mask;
if (end > start)
{
// start...end
for (int i = start; i < end; i++)
{
yield return buffer[i];
}
}
else
{
// start...
for (int i = start; i < buffer.Length; i++)
{
yield return buffer[i];
}
// 0...end
for (int i = 0; i < end; i++)
{
yield return buffer[i];
}
}
}
public IEnumerable<T> Reverse()
{
var start = head & mask;
var end = (head + count) & mask;
if (end > start)
{
// end...start
for (int i = end - 1; i >= start; i--)
{
yield return buffer[i];
}
}
else
{
// end...0
for (int i = end - 1; i >= 0; i--)
{
yield return buffer[i];
}
// ...start
for (int i = buffer.Length - 1; i >= start; i--)
{
yield return buffer[i];
}
}
}
public bool Contains(T item)
{
return IndexOf(item) != -1;
}
public void CopyTo(T[] array, int arrayIndex)
{
var span = GetSpan();
var dest = array.AsSpan(arrayIndex);
span.First.CopyTo(dest);
span.Second.CopyTo(dest.Slice(span.First.Length));
}
public int IndexOf(T item)
{
var i = 0;
foreach (var v in GetSpan())
{
if (EqualityComparer<T>.Default.Equals(item, v))
{
return i;
}
i++;
}
return -1;
}
public T[] ToArray()
{
var result = new T[count];
var i = 0;
foreach (var item in GetSpan())
{
result[i++] = item;
}
return result;
}
void IList<T>.Insert(int index, T item)
{
throw new NotSupportedException();
}
bool ICollection<T>.Remove(T item)
{
throw new NotSupportedException();
}
void IList<T>.RemoveAt(int index)
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
[DoesNotReturn]
static void ThrowForEmpty()
{
throw new InvalidOperationException("RingBuffer is empty.");
}
}
public ref struct RingBufferSpan<T>
{
public readonly ReadOnlySpan<T> First;
public readonly ReadOnlySpan<T> Second;
public readonly int Count;
internal RingBufferSpan(ReadOnlySpan<T> first, ReadOnlySpan<T> second, int count)
{
First = first;
Second = second;
Count = count;
}
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
public ref struct Enumerator
{
ReadOnlySpan<T>.Enumerator firstEnumerator;
ReadOnlySpan<T>.Enumerator secondEnumerator;
bool useFirst;
public Enumerator(RingBufferSpan<T> span)
{
this.firstEnumerator = span.First.GetEnumerator();
this.secondEnumerator = span.Second.GetEnumerator();
this.useFirst = true;
}
public bool MoveNext()
{
if (useFirst)
{
if (firstEnumerator.MoveNext())
{
return true;
}
else
{
useFirst = false;
}
}
if (secondEnumerator.MoveNext())
{
return true;
}
return false;
}
public T Current
{
get
{
if (useFirst)
{
return firstEnumerator.Current;
}
else
{
return secondEnumerator.Current;
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 090e1b8528f51164695f1ed11c685465
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8c8f516891f9df44a847a0bb51a064f0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Collections.Generic
{
internal static class CollectionExtensions
{
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
{
key = kvp.Key;
value = kvp.Value;
}
public static bool Remove<TKey, TValue>(this SortedDictionary<TKey, TValue> dict, TKey key, out TValue value)
{
if (dict.TryGetValue(key, out value))
{
return dict.Remove(key);
}
return false;
}
public static bool Remove<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, out TValue value)
{
if (dict.TryGetValue(key, out value))
{
return dict.Remove(key);
}
return false;
}
#if !NET6_0_OR_GREATER
public static bool TryGetNonEnumeratedCount<T>(this IEnumerable<T> source, out int count)
{
if (source is ICollection<T> collection)
{
count = collection.Count;
return true;
}
if (source is IReadOnlyCollection<T> rCollection)
{
count = rCollection.Count;
return true;
}
count = 0;
return false;
}
#endif
}
#if !NET5_0_OR_GREATER
internal interface IReadOnlySet<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>
{
}
#endif
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02ab78c5678c3bd4ebb45ca99fa0eaea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace System.Runtime.CompilerServices
{
internal static class RuntimeHelpersEx
{
internal static bool IsReferenceOrContainsReferences<T>()
{
#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6
return true;
#else
return RuntimeHelpers.IsReferenceOrContainsReferences<T>();
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90c6c97d9daed56458c4b0f69217c442
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
#if NETSTANDARD2_0 || NET_STANDARD_2_0 || NET_4_6
namespace System.Diagnostics.CodeAnalysis
{
internal sealed class MaybeNullWhenAttribute : Attribute
{
public MaybeNullWhenAttribute(bool returnValue)
{
}
}
internal sealed class DoesNotReturnAttribute : Attribute
{
public DoesNotReturnAttribute()
{
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b96ddbca917d2749a613d7dff847f37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 80babde185fa5f5f58e1c7c451054bf5
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 00334fc99a0c324ce9667086377aec59
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 9faef98abc153627f8af30123e43611c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0bc06841af72151429ea8e5bff9677a0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,206 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 3
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 0
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 0
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 500
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 500
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 2
m_PVRDenoiserTypeDirect: 0
m_PVRDenoiserTypeIndirect: 0
m_PVRDenoiserTypeAO: 0
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 0
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &519420028
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 519420032}
- component: {fileID: 519420031}
- component: {fileID: 519420029}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &519420029
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
m_Enabled: 1
--- !u!20 &519420031
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 2
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 1
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 0
m_HDR: 1
m_AllowMSAA: 0
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 0
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &519420032
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 519420028}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2cda990e2423bbf4892e6590ba056729
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
{
"dependencies": {
"com.unity.collab-proxy": "1.2.16",
"com.unity.ext.nunit": "1.0.0",
"com.unity.ide.rider": "1.1.0",
"com.unity.ide.vscode": "1.1.0",
"com.unity.test-framework": "1.1.1",
"com.unity.textmeshpro": "2.0.1",
"com.unity.timeline": "1.2.2",
"com.unity.ugui": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",
"com.unity.modules.assetbundle": "1.0.0",
"com.unity.modules.audio": "1.0.0",
"com.unity.modules.cloth": "1.0.0",
"com.unity.modules.director": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0",
"com.unity.modules.particlesystem": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.physics2d": "1.0.0",
"com.unity.modules.screencapture": "1.0.0",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.terrainphysics": "1.0.0",
"com.unity.modules.tilemap": "1.0.0",
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.uielements": "1.0.0",
"com.unity.modules.umbra": "1.0.0",
"com.unity.modules.unityanalytics": "1.0.0",
"com.unity.modules.unitywebrequest": "1.0.0",
"com.unity.modules.unitywebrequestassetbundle": "1.0.0",
"com.unity.modules.unitywebrequestaudio": "1.0.0",
"com.unity.modules.unitywebrequesttexture": "1.0.0",
"com.unity.modules.unitywebrequestwww": "1.0.0",
"com.unity.modules.vehicles": "1.0.0",
"com.unity.modules.video": "1.0.0",
"com.unity.modules.vr": "1.0.0",
"com.unity.modules.wind": "1.0.0",
"com.unity.modules.xr": "1.0.0"
}
}

View File

@ -0,0 +1,19 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!11 &1
AudioManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Volume: 1
Rolloff Scale: 1
Doppler Factor: 1
Default Speaker Mode: 2
m_SampleRate: 0
m_DSPBufferSize: 1024
m_VirtualVoiceCount: 512
m_RealVoiceCount: 32
m_SpatializerPlugin:
m_AmbisonicDecoderPlugin:
m_DisableAudio: 0
m_VirtualizeEffects: 1
m_RequestedDSPBufferSize: 0

View File

@ -0,0 +1,6 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!236 &1
ClusterInputManager:
m_ObjectHideFlags: 0
m_Inputs: []

View File

@ -0,0 +1,36 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!55 &1
PhysicsManager:
m_ObjectHideFlags: 0
serializedVersion: 13
m_Gravity: {x: 0, y: -9.81, z: 0}
m_DefaultMaterial: {fileID: 0}
m_BounceThreshold: 2
m_SleepThreshold: 0.005
m_DefaultContactOffset: 0.01
m_DefaultSolverIterations: 6
m_DefaultSolverVelocityIterations: 1
m_QueriesHitBackfaces: 0
m_QueriesHitTriggers: 1
m_EnableAdaptiveForce: 0
m_ClothInterCollisionDistance: 0.1
m_ClothInterCollisionStiffness: 0.2
m_ContactsGeneration: 1
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
m_AutoSimulation: 1
m_AutoSyncTransforms: 0
m_ReuseCollisionCallbacks: 1
m_ClothInterCollisionSettingsToggle: 0
m_ClothGravity: {x: 0, y: -9.81, z: 0}
m_ContactPairsMode: 0
m_BroadphaseType: 0
m_WorldBounds:
m_Center: {x: 0, y: 0, z: 0}
m_Extent: {x: 250, y: 250, z: 250}
m_WorldSubdivisions: 8
m_FrictionType: 0
m_EnableEnhancedDeterminism: 0
m_EnableUnifiedHeightmaps: 1
m_SolverType: 0
m_DefaultMaxAngularSpeed: 50

View File

@ -0,0 +1,11 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1045 &1
EditorBuildSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
- enabled: 1
path: Assets/Scenes/SampleScene.unity
guid: 2cda990e2423bbf4892e6590ba056729
m_configObjects: {}

View File

@ -0,0 +1,36 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!159 &1
EditorSettings:
m_ObjectHideFlags: 0
serializedVersion: 10
m_ExternalVersionControlSupport: Visible Meta Files
m_SerializationMode: 2
m_LineEndingsForNewScripts: 0
m_DefaultBehaviorMode: 1
m_PrefabRegularEnvironment: {fileID: 0}
m_PrefabUIEnvironment: {fileID: 0}
m_SpritePackerMode: 4
m_SpritePackerPaddingPower: 1
m_EtcTextureCompressorBehavior: 1
m_EtcTextureFastCompressor: 1
m_EtcTextureNormalCompressor: 2
m_EtcTextureBestCompressor: 4
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp
m_ProjectGenerationRootNamespace:
m_CollabEditorSettings:
inProgressEnabled: 1
m_EnableTextureStreamingInEditMode: 1
m_EnableTextureStreamingInPlayMode: 1
m_AsyncShaderCompilation: 1
m_EnterPlayModeOptionsEnabled: 0
m_EnterPlayModeOptions: 3
m_ShowLightmapResolutionOverlay: 1
m_UseLegacyProbeSampleCount: 0
m_SerializeInlineMappingsOnOneLine: 1
m_AssetPipelineMode: 1
m_CacheServerMode: 0
m_CacheServerEndpoint:
m_CacheServerNamespacePrefix: default
m_CacheServerEnableDownload: 1
m_CacheServerEnableUpload: 1

View File

@ -0,0 +1,63 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!30 &1
GraphicsSettings:
m_ObjectHideFlags: 0
serializedVersion: 13
m_Deferred:
m_Mode: 1
m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0}
m_DeferredReflections:
m_Mode: 1
m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0}
m_ScreenSpaceShadows:
m_Mode: 1
m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0}
m_LegacyDeferred:
m_Mode: 1
m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0}
m_DepthNormals:
m_Mode: 1
m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0}
m_MotionVectors:
m_Mode: 1
m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0}
m_LightHalo:
m_Mode: 1
m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0}
m_LensFlare:
m_Mode: 1
m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0}
m_VideoShadersIncludeMode: 2
m_AlwaysIncludedShaders:
- {fileID: 7, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0}
m_PreloadedShaders: []
m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
m_CustomRenderPipeline: {fileID: 0}
m_TransparencySortMode: 0
m_TransparencySortAxis: {x: 0, y: 0, z: 1}
m_DefaultRenderingPath: 1
m_DefaultMobileRenderingPath: 1
m_TierSettings: []
m_LightmapStripping: 0
m_FogStripping: 0
m_InstancingStripping: 0
m_LightmapKeepPlain: 1
m_LightmapKeepDirCombined: 1
m_LightmapKeepDynamicPlain: 1
m_LightmapKeepDynamicDirCombined: 1
m_LightmapKeepShadowMask: 1
m_LightmapKeepSubtractive: 1
m_FogKeepLinear: 1
m_FogKeepExp: 1
m_FogKeepExp2: 1
m_AlbedoSwatchInfos: []
m_LightsUseLinearIntensity: 0
m_LightsUseColorTemperature: 0
m_LogWhenShaderIsCompiled: 0

View File

@ -0,0 +1,487 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!13 &1
InputManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Axes:
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton: a
altPositiveButton: d
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton: s
altPositiveButton: w
gravity: 3
dead: 0.001
sensitivity: 3
snap: 1
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left ctrl
altNegativeButton:
altPositiveButton: mouse 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left alt
altNegativeButton:
altPositiveButton: mouse 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left shift
altNegativeButton:
altPositiveButton: mouse 2
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: space
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse X
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Mouse Y
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Mouse ScrollWheel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0
sensitivity: 0.1
snap: 0
invert: 0
type: 1
axis: 2
joyNum: 0
- serializedVersion: 3
m_Name: Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 0
type: 2
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton:
altNegativeButton:
altPositiveButton:
gravity: 0
dead: 0.19
sensitivity: 1
snap: 0
invert: 1
type: 2
axis: 1
joyNum: 0
- serializedVersion: 3
m_Name: Fire1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 0
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 1
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Fire3
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 2
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Jump
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: joystick button 3
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: return
altNegativeButton:
altPositiveButton: joystick button 0
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Submit
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: enter
altNegativeButton:
altPositiveButton: space
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Cancel
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: escape
altNegativeButton:
altPositiveButton: joystick button 1
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Enable Debug Button 1
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left ctrl
altNegativeButton:
altPositiveButton: joystick button 8
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Enable Debug Button 2
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: backspace
altNegativeButton:
altPositiveButton: joystick button 9
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Reset
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left alt
altNegativeButton:
altPositiveButton: joystick button 1
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Next
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: page down
altNegativeButton:
altPositiveButton: joystick button 5
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Previous
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: page up
altNegativeButton:
altPositiveButton: joystick button 4
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Validate
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: return
altNegativeButton:
altPositiveButton: joystick button 0
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Persistent
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: right shift
altNegativeButton:
altPositiveButton: joystick button 2
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Multiplier
descriptiveName:
descriptiveNegativeName:
negativeButton:
positiveButton: left shift
altNegativeButton:
altPositiveButton: joystick button 3
gravity: 0
dead: 0
sensitivity: 0
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 0
axis: 0
joyNum: 0
- serializedVersion: 3
m_Name: Debug Vertical
descriptiveName:
descriptiveNegativeName:
negativeButton: down
positiveButton: up
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 2
axis: 6
joyNum: 0
- serializedVersion: 3
m_Name: Debug Horizontal
descriptiveName:
descriptiveNegativeName:
negativeButton: left
positiveButton: right
altNegativeButton:
altPositiveButton:
gravity: 1000
dead: 0.001
sensitivity: 1000
snap: 0
invert: 0
type: 2
axis: 5
joyNum: 0

View File

@ -0,0 +1,91 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!126 &1
NavMeshProjectSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
areas:
- name: Walkable
cost: 1
- name: Not Walkable
cost: 1
- name: Jump
cost: 2
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
- name:
cost: 1
m_LastAgentTypeID: -887442657
m_Settings:
- serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.75
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
debug:
m_Flags: 0
m_SettingNames:
- Humanoid

View File

@ -0,0 +1,8 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!149 &1
NetworkManager:
m_ObjectHideFlags: 0
m_DebugLevel: 0
m_Sendrate: 15
m_AssetToPrefab: {}

View File

@ -0,0 +1,43 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 61
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_EnablePreviewPackages: 0
m_EnablePackageDependencies: 0
m_AdvancedSettingsExpanded: 1
m_ScopedRegistriesSettingsExpanded: 1
oneTimeWarningShown: 0
m_Registries:
- m_Id: main
m_Name:
m_Url: https://packages.unity.com
m_Scopes: []
m_IsDefault: 1
m_Capabilities: 7
m_UserSelectedRegistryName:
m_UserAddingNewScopedRegistry: 0
m_RegistryInfoDraft:
m_ErrorMessage:
m_Original:
m_Id:
m_Name:
m_Url:
m_Scopes: []
m_IsDefault: 0
m_Capabilities: 0
m_Modified: 0
m_Name:
m_Url:
m_Scopes:
-
m_SelectedScopeIndex: 0

View File

@ -0,0 +1,56 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!19 &1
Physics2DSettings:
m_ObjectHideFlags: 0
serializedVersion: 5
m_Gravity: {x: 0, y: -9.81}
m_DefaultMaterial: {fileID: 0}
m_VelocityIterations: 8
m_PositionIterations: 3
m_VelocityThreshold: 1
m_MaxLinearCorrection: 0.2
m_MaxAngularCorrection: 8
m_MaxTranslationSpeed: 100
m_MaxRotationSpeed: 360
m_BaumgarteScale: 0.2
m_BaumgarteTimeOfImpactScale: 0.75
m_TimeToSleep: 0.5
m_LinearSleepTolerance: 0.01
m_AngularSleepTolerance: 2
m_DefaultContactOffset: 0.01
m_JobOptions:
serializedVersion: 2
useMultithreading: 0
useConsistencySorting: 0
m_InterpolationPosesPerJob: 100
m_NewContactsPerJob: 30
m_CollideContactsPerJob: 100
m_ClearFlagsPerJob: 200
m_ClearBodyForcesPerJob: 200
m_SyncDiscreteFixturesPerJob: 50
m_SyncContinuousFixturesPerJob: 50
m_FindNearestContactsPerJob: 100
m_UpdateTriggerContactsPerJob: 100
m_IslandSolverCostThreshold: 100
m_IslandSolverBodyCostScale: 1
m_IslandSolverContactCostScale: 10
m_IslandSolverJointCostScale: 10
m_IslandSolverBodiesPerJob: 50
m_IslandSolverContactsPerJob: 50
m_SimulationMode: 0
m_QueriesHitTriggers: 1
m_QueriesStartInColliders: 1
m_CallbacksOnDisable: 1
m_ReuseCollisionCallbacks: 1
m_AutoSyncTransforms: 0
m_AlwaysShowColliders: 0
m_ShowColliderSleep: 1
m_ShowColliderContacts: 0
m_ShowColliderAABB: 0
m_ContactArrowScale: 0.2
m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412}
m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432}
m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745}
m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804}
m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

View File

@ -0,0 +1,7 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1386491679 &1
PresetManager:
m_ObjectHideFlags: 0
serializedVersion: 2
m_DefaultPresets: {}

View File

@ -0,0 +1,646 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!129 &1
PlayerSettings:
m_ObjectHideFlags: 0
serializedVersion: 22
productGUID: 4852b04bea0e36b428b29c34acc83ad9
AndroidProfiler: 0
AndroidFilterTouchesWhenObscured: 0
AndroidEnableSustainedPerformanceMode: 0
defaultScreenOrientation: 4
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: DefaultCompany
productName: ObservableCollections.Unity
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
m_ShowUnitySplashScreen: 1
m_ShowUnitySplashLogo: 1
m_SplashScreenOverlayOpacity: 1
m_SplashScreenAnimation: 1
m_SplashScreenLogoStyle: 1
m_SplashScreenDrawMode: 0
m_SplashScreenBackgroundAnimationZoom: 1
m_SplashScreenLogoAnimationZoom: 1
m_SplashScreenBackgroundLandscapeAspect: 1
m_SplashScreenBackgroundPortraitAspect: 1
m_SplashScreenBackgroundLandscapeUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenBackgroundPortraitUvs:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
m_SplashScreenLogos: []
m_VirtualRealitySplashScreen: {fileID: 0}
m_HolographicTrackingLossScreen: {fileID: 0}
defaultScreenWidth: 1920
defaultScreenHeight: 1080
defaultScreenWidthWeb: 960
defaultScreenHeightWeb: 600
m_StereoRenderingPath: 0
m_ActiveColorSpace: 0
m_MTRendering: 1
mipStripping: 0
numberOfMipsStripped: 0
m_StackTraceTypes: 010000000100000001000000010000000100000001000000
iosShowActivityIndicatorOnLoading: -1
androidShowActivityIndicatorOnLoading: -1
iosUseCustomAppBackgroundBehavior: 0
iosAllowHTTPDownload: 1
allowedAutorotateToPortrait: 1
allowedAutorotateToPortraitUpsideDown: 1
allowedAutorotateToLandscapeRight: 1
allowedAutorotateToLandscapeLeft: 1
useOSAutorotation: 1
use32BitDisplayBuffer: 1
preserveFramebufferAlpha: 0
disableDepthAndStencilBuffers: 0
androidStartInFullscreen: 1
androidRenderOutsideSafeArea: 1
androidUseSwappy: 1
androidBlitType: 0
defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 0
captureSingleScreen: 0
muteOtherAudioSources: 0
Prepare IOS For Recording: 0
Force IOS Speakers When Recording: 0
deferSystemGesturesMode: 0
hideHomeButton: 0
submitAnalytics: 1
usePlayerLog: 1
bakeCollisionMeshes: 0
forceSingleInstance: 0
useFlipModelSwapchain: 1
resizableWindow: 0
useMacAppStoreValidation: 0
macAppStoreCategory: public.app-category.games
gpuSkinning: 0
xboxPIXTextureCapture: 0
xboxEnableAvatar: 0
xboxEnableKinect: 0
xboxEnableKinectAutoTracking: 0
xboxEnableFitness: 0
visibleInBackground: 1
allowFullscreenSwitch: 1
fullscreenMode: 1
xboxSpeechDB: 0
xboxEnableHeadOrientation: 0
xboxEnableGuest: 0
xboxEnablePIXSampling: 0
metalFramebufferOnly: 0
xboxOneResolution: 0
xboxOneSResolution: 0
xboxOneXResolution: 3
xboxOneMonoLoggingLevel: 0
xboxOneLoggingLevel: 1
xboxOneDisableEsram: 0
xboxOneEnableTypeOptimization: 0
xboxOnePresentImmediateThreshold: 0
switchQueueCommandMemory: 1048576
switchQueueControlMemory: 16384
switchQueueComputeMemory: 262144
switchNVNShaderPoolsGranularity: 33554432
switchNVNDefaultPoolsGranularity: 16777216
switchNVNOtherPoolsGranularity: 16777216
switchNVNMaxPublicTextureIDCount: 0
switchNVNMaxPublicSamplerIDCount: 0
stadiaPresentMode: 0
stadiaTargetFramerate: 0
vulkanNumSwapchainBuffers: 3
vulkanEnableSetSRGBWrite: 0
vulkanEnablePreTransform: 0
vulkanEnableLateAcquireNextImage: 0
m_SupportedAspectRatios:
4:3: 1
5:4: 1
16:10: 1
16:9: 1
Others: 1
bundleVersion: 1.0
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0
m_HolographicPauseOnTrackingLoss: 1
xboxOneDisableKinectGpuReservation: 1
xboxOneEnable7thCore: 1
vrSettings:
enable360StereoCapture: 0
isWsaHolographicRemotingEnabled: 0
enableFrameTimingStats: 0
useHDRDisplay: 0
D3DHDRBitDepth: 0
m_ColorGamuts: 00000000
targetPixelDensity: 30
resolutionScalingMode: 0
androidSupportedAspectRatio: 1
androidMaxAspectRatio: 2.1
applicationIdentifier: {}
buildNumber:
Standalone: 0
iPhone: 0
tvOS: 0
overrideDefaultApplicationIdentifier: 0
AndroidBundleVersionCode: 1
AndroidMinSdkVersion: 19
AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1
aotOptions:
stripEngineCode: 1
iPhoneStrippingLevel: 0
iPhoneScriptCallOptimization: 0
ForceInternetPermission: 0
ForceSDCardPermission: 0
CreateWallpaper: 0
APKExpansionFiles: 0
keepLoadedShadersAlive: 0
StripUnusedMeshComponents: 0
VertexChannelCompressionMask: 4054
iPhoneSdkVersion: 988
iOSTargetOSVersionString: 11.0
tvOSSdkVersion: 0
tvOSRequireExtendedGameController: 0
tvOSTargetOSVersionString: 11.0
uIPrerenderedIcon: 0
uIRequiresPersistentWiFi: 0
uIRequiresFullScreen: 1
uIStatusBarHidden: 1
uIExitOnSuspend: 0
uIStatusBarStyle: 0
appleTVSplashScreen: {fileID: 0}
appleTVSplashScreen2x: {fileID: 0}
tvOSSmallIconLayers: []
tvOSSmallIconLayers2x: []
tvOSLargeIconLayers: []
tvOSLargeIconLayers2x: []
tvOSTopShelfImageLayers: []
tvOSTopShelfImageLayers2x: []
tvOSTopShelfImageWideLayers: []
tvOSTopShelfImageWideLayers2x: []
iOSLaunchScreenType: 0
iOSLaunchScreenPortrait: {fileID: 0}
iOSLaunchScreenLandscape: {fileID: 0}
iOSLaunchScreenBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreenFillPct: 100
iOSLaunchScreenSize: 100
iOSLaunchScreenCustomXibPath:
iOSLaunchScreeniPadType: 0
iOSLaunchScreeniPadImage: {fileID: 0}
iOSLaunchScreeniPadBackgroundColor:
serializedVersion: 2
rgba: 0
iOSLaunchScreeniPadFillPct: 100
iOSLaunchScreeniPadSize: 100
iOSLaunchScreeniPadCustomXibPath:
iOSLaunchScreenCustomStoryboardPath:
iOSLaunchScreeniPadCustomStoryboardPath:
iOSDeviceRequirements: []
iOSURLSchemes: []
iOSBackgroundModes: 0
iOSMetalForceHardShadows: 0
metalEditorSupport: 1
metalAPIValidation: 1
iOSRenderExtraFrameOnPause: 0
iosCopyPluginsCodeInsteadOfSymlink: 0
appleDeveloperTeamID:
iOSManualSigningProvisioningProfileID:
tvOSManualSigningProvisioningProfileID:
iOSManualSigningProvisioningProfileType: 0
tvOSManualSigningProvisioningProfileType: 0
appleEnableAutomaticSigning: 0
iOSRequireARKit: 0
iOSAutomaticallyDetectAndAddCapabilities: 1
appleEnableProMotion: 0
shaderPrecisionModel: 0
clonedFromGUID: 10ad67313f4034357812315f3c407484
templatePackageId: com.unity.template.2d@5.0.0
templateDefaultScene: Assets/Scenes/SampleScene.unity
useCustomMainManifest: 0
useCustomLauncherManifest: 0
useCustomMainGradleTemplate: 0
useCustomLauncherGradleManifest: 0
useCustomBaseGradleTemplate: 0
useCustomGradlePropertiesTemplate: 0
useCustomProguardFile: 0
AndroidTargetArchitectures: 1
AndroidSplashScreenScale: 0
androidSplashScreen: {fileID: 0}
AndroidKeystoreName:
AndroidKeyaliasName:
AndroidBuildApkPerCpuArchitecture: 0
AndroidTVCompatibility: 0
AndroidIsGame: 1
AndroidEnableTango: 0
androidEnableBanner: 1
androidUseLowAccuracyLocation: 0
androidUseCustomKeystore: 0
m_AndroidBanners:
- width: 320
height: 180
banner: {fileID: 0}
androidGamepadSupportLevel: 0
AndroidMinifyWithR8: 0
AndroidMinifyRelease: 0
AndroidMinifyDebug: 0
AndroidValidateAppBundleSize: 1
AndroidAppBundleSizeToValidate: 150
m_BuildTargetIcons: []
m_BuildTargetPlatformIcons: []
m_BuildTargetBatching: []
m_BuildTargetGraphicsJobs:
- m_BuildTarget: MacStandaloneSupport
m_GraphicsJobs: 0
- m_BuildTarget: Switch
m_GraphicsJobs: 0
- m_BuildTarget: MetroSupport
m_GraphicsJobs: 0
- m_BuildTarget: AppleTVSupport
m_GraphicsJobs: 0
- m_BuildTarget: BJMSupport
m_GraphicsJobs: 0
- m_BuildTarget: LinuxStandaloneSupport
m_GraphicsJobs: 0
- m_BuildTarget: PS4Player
m_GraphicsJobs: 0
- m_BuildTarget: iOSSupport
m_GraphicsJobs: 0
- m_BuildTarget: WindowsStandaloneSupport
m_GraphicsJobs: 0
- m_BuildTarget: XboxOnePlayer
m_GraphicsJobs: 0
- m_BuildTarget: LuminSupport
m_GraphicsJobs: 0
- m_BuildTarget: AndroidPlayer
m_GraphicsJobs: 0
- m_BuildTarget: WebGLSupport
m_GraphicsJobs: 0
m_BuildTargetGraphicsJobMode: []
m_BuildTargetGraphicsAPIs:
- m_BuildTarget: AndroidPlayer
m_APIs: 150000000b000000
m_Automatic: 0
- m_BuildTarget: iOSSupport
m_APIs: 10000000
m_Automatic: 1
m_BuildTargetVRSettings: []
openGLRequireES31: 0
openGLRequireES31AEP: 0
openGLRequireES32: 0
m_TemplateCustomTags: {}
mobileMTRendering:
Android: 1
iPhone: 1
tvOS: 1
m_BuildTargetGroupLightmapEncodingQuality: []
m_BuildTargetGroupLightmapSettings: []
m_BuildTargetNormalMapEncoding: []
playModeTestRunnerEnabled: 0
runPlayModeTestAsEditModeTest: 0
actionOnDotNetUnhandledException: 1
enableInternalProfiler: 0
logObjCUncaughtExceptions: 1
enableCrashReportAPI: 0
cameraUsageDescription:
locationUsageDescription:
microphoneUsageDescription:
switchNMETAOverride:
switchNetLibKey:
switchSocketMemoryPoolSize: 6144
switchSocketAllocatorPoolSize: 128
switchSocketConcurrencyLimit: 14
switchScreenResolutionBehavior: 2
switchUseCPUProfiler: 0
switchUseGOLDLinker: 0
switchApplicationID: 0x01004b9000490000
switchNSODependencies:
switchTitleNames_0:
switchTitleNames_1:
switchTitleNames_2:
switchTitleNames_3:
switchTitleNames_4:
switchTitleNames_5:
switchTitleNames_6:
switchTitleNames_7:
switchTitleNames_8:
switchTitleNames_9:
switchTitleNames_10:
switchTitleNames_11:
switchTitleNames_12:
switchTitleNames_13:
switchTitleNames_14:
switchTitleNames_15:
switchPublisherNames_0:
switchPublisherNames_1:
switchPublisherNames_2:
switchPublisherNames_3:
switchPublisherNames_4:
switchPublisherNames_5:
switchPublisherNames_6:
switchPublisherNames_7:
switchPublisherNames_8:
switchPublisherNames_9:
switchPublisherNames_10:
switchPublisherNames_11:
switchPublisherNames_12:
switchPublisherNames_13:
switchPublisherNames_14:
switchPublisherNames_15:
switchIcons_0: {fileID: 0}
switchIcons_1: {fileID: 0}
switchIcons_2: {fileID: 0}
switchIcons_3: {fileID: 0}
switchIcons_4: {fileID: 0}
switchIcons_5: {fileID: 0}
switchIcons_6: {fileID: 0}
switchIcons_7: {fileID: 0}
switchIcons_8: {fileID: 0}
switchIcons_9: {fileID: 0}
switchIcons_10: {fileID: 0}
switchIcons_11: {fileID: 0}
switchIcons_12: {fileID: 0}
switchIcons_13: {fileID: 0}
switchIcons_14: {fileID: 0}
switchIcons_15: {fileID: 0}
switchSmallIcons_0: {fileID: 0}
switchSmallIcons_1: {fileID: 0}
switchSmallIcons_2: {fileID: 0}
switchSmallIcons_3: {fileID: 0}
switchSmallIcons_4: {fileID: 0}
switchSmallIcons_5: {fileID: 0}
switchSmallIcons_6: {fileID: 0}
switchSmallIcons_7: {fileID: 0}
switchSmallIcons_8: {fileID: 0}
switchSmallIcons_9: {fileID: 0}
switchSmallIcons_10: {fileID: 0}
switchSmallIcons_11: {fileID: 0}
switchSmallIcons_12: {fileID: 0}
switchSmallIcons_13: {fileID: 0}
switchSmallIcons_14: {fileID: 0}
switchSmallIcons_15: {fileID: 0}
switchManualHTML:
switchAccessibleURLs:
switchLegalInformation:
switchMainThreadStackSize: 1048576
switchPresenceGroupId:
switchLogoHandling: 0
switchReleaseVersion: 0
switchDisplayVersion: 1.0.0
switchStartupUserAccount: 0
switchTouchScreenUsage: 0
switchSupportedLanguagesMask: 0
switchLogoType: 0
switchApplicationErrorCodeCategory:
switchUserAccountSaveDataSize: 0
switchUserAccountSaveDataJournalSize: 0
switchApplicationAttribute: 0
switchCardSpecSize: -1
switchCardSpecClock: -1
switchRatingsMask: 0
switchRatingsInt_0: 0
switchRatingsInt_1: 0
switchRatingsInt_2: 0
switchRatingsInt_3: 0
switchRatingsInt_4: 0
switchRatingsInt_5: 0
switchRatingsInt_6: 0
switchRatingsInt_7: 0
switchRatingsInt_8: 0
switchRatingsInt_9: 0
switchRatingsInt_10: 0
switchRatingsInt_11: 0
switchRatingsInt_12: 0
switchLocalCommunicationIds_0:
switchLocalCommunicationIds_1:
switchLocalCommunicationIds_2:
switchLocalCommunicationIds_3:
switchLocalCommunicationIds_4:
switchLocalCommunicationIds_5:
switchLocalCommunicationIds_6:
switchLocalCommunicationIds_7:
switchParentalControl: 0
switchAllowsScreenshot: 1
switchAllowsVideoCapturing: 1
switchAllowsRuntimeAddOnContentInstall: 0
switchDataLossConfirmation: 0
switchUserAccountLockEnabled: 0
switchSystemResourceMemory: 16777216
switchSupportedNpadStyles: 22
switchNativeFsCacheSize: 32
switchIsHoldTypeHorizontal: 0
switchSupportedNpadCount: 8
switchSocketConfigEnabled: 0
switchTcpInitialSendBufferSize: 32
switchTcpInitialReceiveBufferSize: 64
switchTcpAutoSendBufferSizeMax: 256
switchTcpAutoReceiveBufferSizeMax: 256
switchUdpSendBufferSize: 9
switchUdpReceiveBufferSize: 42
switchSocketBufferEfficiency: 4
switchSocketInitializeEnabled: 1
switchNetworkInterfaceManagerInitializeEnabled: 1
switchPlayerConnectionEnabled: 1
switchUseNewStyleFilepaths: 0
switchUseMicroSleepForYield: 1
switchMicroSleepForYieldTime: 25
ps4NPAgeRating: 12
ps4NPTitleSecret:
ps4NPTrophyPackPath:
ps4ParentalLevel: 11
ps4ContentID: ED1633-NPXX51362_00-0000000000000000
ps4Category: 0
ps4MasterVersion: 01.00
ps4AppVersion: 01.00
ps4AppType: 0
ps4ParamSfxPath:
ps4VideoOutPixelFormat: 0
ps4VideoOutInitialWidth: 1920
ps4VideoOutBaseModeInitialWidth: 1920
ps4VideoOutReprojectionRate: 60
ps4PronunciationXMLPath:
ps4PronunciationSIGPath:
ps4BackgroundImagePath:
ps4StartupImagePath:
ps4StartupImagesFolder:
ps4IconImagesFolder:
ps4SaveDataImagePath:
ps4SdkOverride:
ps4BGMPath:
ps4ShareFilePath:
ps4ShareOverlayImagePath:
ps4PrivacyGuardImagePath:
ps4ExtraSceSysFile:
ps4NPtitleDatPath:
ps4RemotePlayKeyAssignment: -1
ps4RemotePlayKeyMappingDir:
ps4PlayTogetherPlayerCount: 0
ps4EnterButtonAssignment: 2
ps4ApplicationParam1: 0
ps4ApplicationParam2: 0
ps4ApplicationParam3: 0
ps4ApplicationParam4: 0
ps4DownloadDataSize: 0
ps4GarlicHeapSize: 2048
ps4ProGarlicHeapSize: 2560
playerPrefsMaxSize: 32768
ps4Passcode: bi9UOuSpM2Tlh01vOzwvSikHFswuzleh
ps4pnSessions: 1
ps4pnPresence: 1
ps4pnFriends: 1
ps4pnGameCustomData: 1
playerPrefsSupport: 0
enableApplicationExit: 0
resetTempFolder: 1
restrictedAudioUsageRights: 0
ps4UseResolutionFallback: 0
ps4ReprojectionSupport: 0
ps4UseAudio3dBackend: 0
ps4UseLowGarlicFragmentationMode: 1
ps4SocialScreenEnabled: 0
ps4ScriptOptimizationLevel: 2
ps4Audio3dVirtualSpeakerCount: 14
ps4attribCpuUsage: 0
ps4PatchPkgPath:
ps4PatchLatestPkgPath:
ps4PatchChangeinfoPath:
ps4PatchDayOne: 0
ps4attribUserManagement: 0
ps4attribMoveSupport: 0
ps4attrib3DSupport: 0
ps4attribShareSupport: 0
ps4attribExclusiveVR: 0
ps4disableAutoHideSplash: 0
ps4videoRecordingFeaturesUsed: 0
ps4contentSearchFeaturesUsed: 0
ps4CompatibilityPS5: 0
ps4GPU800MHz: 1
ps4attribEyeToEyeDistanceSettingVR: 0
ps4IncludedModules: []
ps4attribVROutputEnabled: 0
monoEnv:
splashScreenBackgroundSourceLandscape: {fileID: 0}
splashScreenBackgroundSourcePortrait: {fileID: 0}
blurSplashScreenBackground: 1
spritePackerPolicy:
webGLMemorySize: 32
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLDataCaching: 1
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:Default
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 0
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1
webGLThreadsSupport: 0
webGLDecompressionFallback: 0
scriptingDefineSymbols: {}
additionalCompilerArguments: {}
platformArchitecture: {}
scriptingBackend: {}
il2cppCompilerConfiguration: {}
managedStrippingLevel: {}
incrementalIl2cppBuild: {}
suppressCommonWarnings: 1
allowUnsafeCode: 0
useDeterministicCompilation: 1
useReferenceAssemblies: 1
enableRoslynAnalyzers: 1
additionalIl2CppArgs:
scriptingRuntimeVersion: 1
gcIncremental: 1
assemblyVersionValidation: 1
gcWBarrierValidation: 0
apiCompatibilityLevelPerPlatform: {}
m_RenderingPath: 1
m_MobileRenderingPath: 1
metroPackageName: 2D_BuiltInRenderer
metroPackageVersion:
metroCertificatePath:
metroCertificatePassword:
metroCertificateSubject:
metroCertificateIssuer:
metroCertificateNotAfter: 0000000000000000
metroApplicationDescription: 2D_BuiltInRenderer
wsaImages: {}
metroTileShortName:
metroTileShowName: 0
metroMediumTileShowName: 0
metroLargeTileShowName: 0
metroWideTileShowName: 0
metroSupportStreamingInstall: 0
metroLastRequiredScene: 0
metroDefaultTileSize: 1
metroTileForegroundText: 2
metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0}
metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1}
metroSplashScreenUseBackgroundColor: 0
platformCapabilities: {}
metroTargetDeviceFamilies: {}
metroFTAName:
metroFTAFileTypes: []
metroProtocolName:
XboxOneProductId:
XboxOneUpdateKey:
XboxOneSandboxId:
XboxOneContentId:
XboxOneTitleId:
XboxOneSCId:
XboxOneGameOsOverridePath:
XboxOnePackagingOverridePath:
XboxOneAppManifestOverridePath:
XboxOneVersion: 1.0.0.0
XboxOnePackageEncryption: 0
XboxOnePackageUpdateGranularity: 2
XboxOneDescription:
XboxOneLanguage:
- enus
XboxOneCapability: []
XboxOneGameRating: {}
XboxOneIsContentPackage: 0
XboxOneEnhancedXboxCompatibilityMode: 0
XboxOneEnableGPUVariability: 1
XboxOneSockets: {}
XboxOneSplashScreen: {fileID: 0}
XboxOneAllowedProductIds: []
XboxOnePersistentLocalStorageSize: 0
XboxOneXTitleMemory: 8
XboxOneOverrideIdentityName:
XboxOneOverrideIdentityPublisher:
vrEditorSettings: {}
cloudServicesEnabled: {}
luminIcon:
m_Name:
m_ModelFolderPath:
m_PortalFolderPath:
luminCert:
m_CertPath:
m_SignPackage: 1
luminIsChannelApp: 0
luminVersion:
m_VersionCode: 1
m_VersionName:
apiCompatibilityLevel: 6
activeInputHandler: 0
cloudProjectId:
framebufferDepthMemorylessMode: 0
qualitySettingsNames: []
projectName:
organizationId:
cloudEnabled: 0
legacyClampBlendShapeWeights: 0
virtualTexturingSupportEnabled: 0

View File

@ -0,0 +1,2 @@
m_EditorVersion: 2020.1.0a3
m_EditorVersionWithRevision: 2020.1.0a3 (a37e4d4d532f)

View File

@ -0,0 +1,236 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!47 &1
QualitySettings:
m_ObjectHideFlags: 0
serializedVersion: 5
m_CurrentQuality: 5
m_QualitySettings:
- serializedVersion: 2
name: Very Low
pixelLightCount: 0
shadows: 0
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 15
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
skinWeights: 1
textureQuality: 1
anisotropicTextures: 0
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 0
lodBias: 0.3
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 4
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
- serializedVersion: 2
name: Low
pixelLightCount: 0
shadows: 0
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 20
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
skinWeights: 2
textureQuality: 0
anisotropicTextures: 0
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 0
lodBias: 0.4
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 16
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
- serializedVersion: 2
name: Medium
pixelLightCount: 1
shadows: 1
shadowResolution: 0
shadowProjection: 1
shadowCascades: 1
shadowDistance: 20
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 0
skinWeights: 2
textureQuality: 0
anisotropicTextures: 1
antiAliasing: 0
softParticles: 0
softVegetation: 0
realtimeReflectionProbes: 0
billboardsFaceCameraPosition: 0
vSyncCount: 1
lodBias: 0.7
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 64
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
- serializedVersion: 2
name: High
pixelLightCount: 2
shadows: 2
shadowResolution: 1
shadowProjection: 1
shadowCascades: 2
shadowDistance: 40
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
skinWeights: 2
textureQuality: 0
anisotropicTextures: 1
antiAliasing: 0
softParticles: 0
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 1
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 256
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
- serializedVersion: 2
name: Very High
pixelLightCount: 3
shadows: 2
shadowResolution: 2
shadowProjection: 1
shadowCascades: 2
shadowDistance: 70
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
skinWeights: 4
textureQuality: 0
anisotropicTextures: 2
antiAliasing: 2
softParticles: 1
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 1.5
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 1024
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
- serializedVersion: 2
name: Ultra
pixelLightCount: 4
shadows: 2
shadowResolution: 2
shadowProjection: 1
shadowCascades: 4
shadowDistance: 150
shadowNearPlaneOffset: 3
shadowCascade2Split: 0.33333334
shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667}
shadowmaskMode: 1
skinWeights: 255
textureQuality: 0
anisotropicTextures: 2
antiAliasing: 2
softParticles: 1
softVegetation: 1
realtimeReflectionProbes: 1
billboardsFaceCameraPosition: 1
vSyncCount: 1
lodBias: 2
maximumLODLevel: 0
streamingMipmapsActive: 0
streamingMipmapsAddAllCameras: 1
streamingMipmapsMemoryBudget: 512
streamingMipmapsRenderersPerFrame: 512
streamingMipmapsMaxLevelReduction: 2
streamingMipmapsMaxFileIORequests: 1024
particleRaycastBudget: 4096
asyncUploadTimeSlice: 2
asyncUploadBufferSize: 16
asyncUploadPersistentBuffer: 1
resolutionScalingFixedDPIFactor: 1
customRenderPipeline: {fileID: 0}
excludedTargetPlatforms: []
m_PerPlatformDefaultQuality:
Android: 2
Lumin: 5
Nintendo Switch: 5
PS4: 5
Stadia: 5
Standalone: 5
WebGL: 3
Windows Store Apps: 5
XboxOne: 5
iPhone: 2
tvOS: 2

View File

@ -0,0 +1,43 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!78 &1
TagManager:
serializedVersion: 2
tags: []
layers:
- Default
- TransparentFX
- Ignore Raycast
-
- Water
- UI
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
m_SortingLayers:
- name: Default
uniqueID: 0
locked: 0

View File

@ -0,0 +1,9 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!5 &1
TimeManager:
m_ObjectHideFlags: 0
Fixed Timestep: 0.02
Maximum Allowed Timestep: 0.33333334
m_TimeScale: 1
Maximum Particle Timestep: 0.03

View File

@ -0,0 +1,35 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!310 &1
UnityConnectSettings:
m_ObjectHideFlags: 0
serializedVersion: 1
m_Enabled: 0
m_TestMode: 0
m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events
m_EventUrl: https://cdp.cloud.unity3d.com/v1/events
m_ConfigUrl: https://config.uca.cloud.unity3d.com
m_DashboardUrl: https://dashboard.unity3d.com
m_TestInitMode: 0
CrashReportingSettings:
m_EventUrl: https://perf-events.cloud.unity3d.com
m_Enabled: 0
m_LogBufferSize: 10
m_CaptureEditorExceptions: 1
UnityPurchasingSettings:
m_Enabled: 0
m_TestMode: 0
UnityAnalyticsSettings:
m_Enabled: 0
m_TestMode: 0
m_InitializeOnStartup: 1
UnityAdsSettings:
m_Enabled: 0
m_InitializeOnStartup: 1
m_TestMode: 0
m_IosGameId:
m_AndroidGameId:
m_GameIds: {}
m_GameId:
PerformanceReportingSettings:
m_Enabled: 0

View File

@ -0,0 +1,12 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!937362698 &1
VFXManager:
m_ObjectHideFlags: 0
m_IndirectShader: {fileID: 0}
m_CopyBufferShader: {fileID: 0}
m_SortShader: {fileID: 0}
m_StripUpdateShader: {fileID: 0}
m_RenderPipeSettingsPath:
m_FixedTimeStep: 0.016666668
m_MaxDeltaTime: 0.05

View File

@ -0,0 +1,8 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!890905787 &1
VersionControlSettings:
m_ObjectHideFlags: 0
m_Mode: Visible Meta Files
m_CollabEditorSettings:
inProgressEnabled: 1

View File

@ -0,0 +1,10 @@
{
"m_SettingKeys": [
"VR Device Disabled",
"VR Device User Alert"
],
"m_SettingValues": [
"False",
"False"
]
}

View File

@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!162 &1
EditorUserSettings:
m_ObjectHideFlags: 0
serializedVersion: 4
m_ConfigSettings:
RecentlyUsedScenePath-0:
value: 22424703114646680e0b0227036c6c111b07142f1f2b233e2867083debf42d
flags: 0
vcSharedLogLevel:
value: 0d5e400f0650
flags: 0
m_VCAutomaticAdd: 1
m_VCDebugCom: 0
m_VCDebugCmd: 0
m_VCDebugOut: 0
m_SemanticMergeMode: 2
m_VCShowFailedCheckout: 1
m_VCOverwriteFailedCheckoutAssets: 1
m_VCProjectOverlayIcons: 1
m_VCHierarchyOverlayIcons: 1
m_VCOtherOverlayIcons: 1
m_VCAllowAsyncUpdate: 1

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ConsoleAppFramework" Version="3.3.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,62 @@
using ConsoleAppFramework;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace PostBuildUtility
{
class Program : ConsoleAppBase
{
static async Task Main(string[] args)
{
await Host.CreateDefaultBuilder().RunConsoleAppFrameworkAsync<Program>(args);
}
[Command("replace-to-unity")]
public void ReplaceToUnity([Option(0)] string directory)
{
var replaceSet = new Dictionary<string, string>
{
// Remove nullable
{"#nullable disable", "" },
{"where T : notnull", "" },
{"where TKey : notnull", "" },
{">?", ">" }, // generics <T>?
{"T?", "T" },
{"T[]?", "T[]" },
{"default!", "default" },
{"null!", "null" },
// project specified
{"array!", "array" },
{"Current!", "Current" },
{"NotifyCollectionChangedEventHandler?", "NotifyCollectionChangedEventHandler" },
{"PropertyChangedEventHandler?", "PropertyChangedEventHandler" },
};
System.Console.WriteLine("Start to replace code, remove nullability.");
var noBomUtf8 = new UTF8Encoding(false);
foreach (var path in Directory.EnumerateFiles(directory, "*.cs", SearchOption.AllDirectories))
{
var text = File.ReadAllText(path, Encoding.UTF8);
var original = text;
foreach (var item in replaceSet)
{
text = text.Replace(item.Key, item.Value);
}
if (text != original)
{
File.WriteAllText(path, text, noBomUtf8);
}
}
System.Console.WriteLine("Replace complete.");
}
}
}