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();
- }
- }
|