| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 | using System;using System.Collections;using System.Collections.Generic;namespace Mirror{    public class SyncSet<T> : ISet<T>, SyncObject    {        public delegate void SyncSetChanged(Operation op, T item);        protected readonly ISet<T> objects;        public int Count => objects.Count;        public bool IsReadOnly { get; private set; }        public event SyncSetChanged Callback;        public enum Operation : byte        {            OP_ADD,            OP_CLEAR,            OP_REMOVE        }        struct Change        {            internal Operation operation;            internal T item;        }        readonly List<Change> changes = new List<Change>();        // how many changes we need to ignore        // this is needed because when we initialize the list,        // we might later receive changes that have already been applied        // so we need to skip them        int changesAhead;        public SyncSet(ISet<T> objects)        {            this.objects = objects;        }        public void Reset()        {            IsReadOnly = false;            changes.Clear();            changesAhead = 0;            objects.Clear();        }        public bool IsDirty => changes.Count > 0;        // throw away all the changes        // this should be called after a successful sync        public void Flush() => changes.Clear();        void AddOperation(Operation op, T item)        {            if (IsReadOnly)            {                throw new InvalidOperationException("SyncSets can only be modified at the server");            }            Change change = new Change            {                operation = op,                item = item            };            changes.Add(change);            Callback?.Invoke(op, item);        }        void AddOperation(Operation op) => AddOperation(op, default);        public void OnSerializeAll(NetworkWriter writer)        {            // if init,  write the full list content            writer.WriteUInt((uint)objects.Count);            foreach (T obj in objects)            {                writer.Write(obj);            }            // all changes have been applied already            // thus the client will need to skip all the pending changes            // or they would be applied again.            // So we write how many changes are pending            writer.WriteUInt((uint)changes.Count);        }        public void OnSerializeDelta(NetworkWriter writer)        {            // write all the queued up changes            writer.WriteUInt((uint)changes.Count);            for (int i = 0; i < changes.Count; i++)            {                Change change = changes[i];                writer.WriteByte((byte)change.operation);                switch (change.operation)                {                    case Operation.OP_ADD:                        writer.Write(change.item);                        break;                    case Operation.OP_CLEAR:                        break;                    case Operation.OP_REMOVE:                        writer.Write(change.item);                        break;                }            }        }        public void OnDeserializeAll(NetworkReader reader)        {            // This list can now only be modified by synchronization            IsReadOnly = true;            // if init,  write the full list content            int count = (int)reader.ReadUInt();            objects.Clear();            changes.Clear();            for (int i = 0; i < count; i++)            {                T obj = reader.Read<T>();                objects.Add(obj);            }            // We will need to skip all these changes            // the next time the list is synchronized            // because they have already been applied            changesAhead = (int)reader.ReadUInt();        }        public void OnDeserializeDelta(NetworkReader reader)        {            // This list can now only be modified by synchronization            IsReadOnly = true;            int changesCount = (int)reader.ReadUInt();            for (int i = 0; i < changesCount; i++)            {                Operation operation = (Operation)reader.ReadByte();                // apply the operation only if it is a new change                // that we have not applied yet                bool apply = changesAhead == 0;                T item = default;                switch (operation)                {                    case Operation.OP_ADD:                        item = reader.Read<T>();                        if (apply)                        {                            objects.Add(item);                        }                        break;                    case Operation.OP_CLEAR:                        if (apply)                        {                            objects.Clear();                        }                        break;                    case Operation.OP_REMOVE:                        item = reader.Read<T>();                        if (apply)                        {                            objects.Remove(item);                        }                        break;                }                if (apply)                {                    Callback?.Invoke(operation, item);                }                // we just skipped this change                else                {                    changesAhead--;                }            }        }        public bool Add(T item)        {            if (objects.Add(item))            {                AddOperation(Operation.OP_ADD, item);                return true;            }            return false;        }        void ICollection<T>.Add(T item)        {            if (objects.Add(item))            {                AddOperation(Operation.OP_ADD, item);            }        }        public void Clear()        {            objects.Clear();            AddOperation(Operation.OP_CLEAR);        }        public bool Contains(T item) => objects.Contains(item);        public void CopyTo(T[] array, int index) => objects.CopyTo(array, index);        public bool Remove(T item)        {            if (objects.Remove(item))            {                AddOperation(Operation.OP_REMOVE, item);                return true;            }            return false;        }        public IEnumerator<T> GetEnumerator() => objects.GetEnumerator();        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();        public void ExceptWith(IEnumerable<T> other)        {            if (other == this)            {                Clear();                return;            }            // remove every element in other from this            foreach (T element in other)            {                Remove(element);            }        }        public void IntersectWith(IEnumerable<T> other)        {            if (other is ISet<T> otherSet)            {                IntersectWithSet(otherSet);            }            else            {                HashSet<T> otherAsSet = new HashSet<T>(other);                IntersectWithSet(otherAsSet);            }        }        void IntersectWithSet(ISet<T> otherSet)        {            List<T> elements = new List<T>(objects);            foreach (T element in elements)            {                if (!otherSet.Contains(element))                {                    Remove(element);                }            }        }        public bool IsProperSubsetOf(IEnumerable<T> other) => objects.IsProperSubsetOf(other);        public bool IsProperSupersetOf(IEnumerable<T> other) => objects.IsProperSupersetOf(other);        public bool IsSubsetOf(IEnumerable<T> other) => objects.IsSubsetOf(other);        public bool IsSupersetOf(IEnumerable<T> other) => objects.IsSupersetOf(other);        public bool Overlaps(IEnumerable<T> other) => objects.Overlaps(other);        public bool SetEquals(IEnumerable<T> other) => objects.SetEquals(other);        // custom implementation so we can do our own Clear/Add/Remove for delta        public void SymmetricExceptWith(IEnumerable<T> other)        {            if (other == this)            {                Clear();            }            else            {                foreach (T element in other)                {                    if (!Remove(element))                    {                        Add(element);                    }                }            }        }        // custom implementation so we can do our own Clear/Add/Remove for delta        public void UnionWith(IEnumerable<T> other)        {            if (other != this)            {                foreach (T element in other)                {                    Add(element);                }            }        }    }    public class SyncHashSet<T> : SyncSet<T>    {        public SyncHashSet() : this(EqualityComparer<T>.Default) {}        public SyncHashSet(IEqualityComparer<T> comparer) : base(new HashSet<T>(comparer ?? EqualityComparer<T>.Default)) {}        // allocation free enumerator        public new HashSet<T>.Enumerator GetEnumerator() => ((HashSet<T>)objects).GetEnumerator();    }    public class SyncSortedSet<T> : SyncSet<T>    {        public SyncSortedSet() : this(Comparer<T>.Default) {}        public SyncSortedSet(IComparer<T> comparer) : base(new SortedSet<T>(comparer ?? Comparer<T>.Default)) {}        // allocation free enumerator        public new SortedSet<T>.Enumerator GetEnumerator() => ((SortedSet<T>)objects).GetEnumerator();    }}
 |