| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 | using System.Collections;using System.Collections.Generic;using JetBrains.Annotations;namespace Mirror{    public class SyncIDictionary<TKey, TValue> : IDictionary<TKey, TValue>, SyncObject, IReadOnlyDictionary<TKey, TValue>    {        public delegate void SyncDictionaryChanged(Operation op, TKey key, TValue item);        protected readonly IDictionary<TKey, TValue> objects;        public int Count => objects.Count;        public bool IsReadOnly { get; private set; }        public event SyncDictionaryChanged Callback;        public enum Operation : byte        {            OP_ADD,            OP_CLEAR,            OP_REMOVE,            OP_SET        }        struct Change        {            internal Operation operation;            internal TKey key;            internal TValue 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 void Reset()        {            IsReadOnly = false;            changes.Clear();            changesAhead = 0;            objects.Clear();        }        public bool IsDirty => changes.Count > 0;        public ICollection<TKey> Keys => objects.Keys;        public ICollection<TValue> Values => objects.Values;        IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => objects.Keys;        IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => objects.Values;        // throw away all the changes        // this should be called after a successful sync        public void Flush() => changes.Clear();        public SyncIDictionary(IDictionary<TKey, TValue> objects)        {            this.objects = objects;        }        void AddOperation(Operation op, TKey key, TValue item)        {            if (IsReadOnly)            {                throw new System.InvalidOperationException("SyncDictionaries can only be modified by the server");            }            Change change = new Change            {                operation = op,                key = key,                item = item            };            changes.Add(change);            Callback?.Invoke(op, key, item);        }        public void OnSerializeAll(NetworkWriter writer)        {            // if init, write the full list content            writer.WriteUInt((uint)objects.Count);            foreach (KeyValuePair<TKey, TValue> syncItem in objects)            {                writer.Write(syncItem.Key);                writer.Write(syncItem.Value);            }            // 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:                    case Operation.OP_REMOVE:                    case Operation.OP_SET:                        writer.Write(change.key);                        writer.Write(change.item);                        break;                    case Operation.OP_CLEAR:                        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++)            {                TKey key = reader.Read<TKey>();                TValue obj = reader.Read<TValue>();                objects.Add(key, 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;                TKey key = default;                TValue item = default;                switch (operation)                {                    case Operation.OP_ADD:                    case Operation.OP_SET:                        key = reader.Read<TKey>();                        item = reader.Read<TValue>();                        if (apply)                        {                            objects[key] = item;                        }                        break;                    case Operation.OP_CLEAR:                        if (apply)                        {                            objects.Clear();                        }                        break;                    case Operation.OP_REMOVE:                        key = reader.Read<TKey>();                        item = reader.Read<TValue>();                        if (apply)                        {                            objects.Remove(key);                        }                        break;                }                if (apply)                {                    Callback?.Invoke(operation, key, item);                }                // we just skipped this change                else                {                    changesAhead--;                }            }        }        public void Clear()        {            objects.Clear();            AddOperation(Operation.OP_CLEAR, default, default);        }        public bool ContainsKey(TKey key) => objects.ContainsKey(key);        public bool Remove(TKey key)        {            if (objects.TryGetValue(key, out TValue item) && objects.Remove(key))            {                AddOperation(Operation.OP_REMOVE, key, item);                return true;            }            return false;        }        public TValue this[TKey i]        {            get => objects[i];            set            {                if (ContainsKey(i))                {                    objects[i] = value;                    AddOperation(Operation.OP_SET, i, value);                }                else                {                    objects[i] = value;                    AddOperation(Operation.OP_ADD, i, value);                }            }        }        public bool TryGetValue(TKey key, out TValue value) => objects.TryGetValue(key, out value);        public void Add(TKey key, TValue value)        {            objects.Add(key, value);            AddOperation(Operation.OP_ADD, key, value);        }        public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);        public bool Contains(KeyValuePair<TKey, TValue> item)        {            return TryGetValue(item.Key, out TValue val) && EqualityComparer<TValue>.Default.Equals(val, item.Value);        }        public void CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)        {            if (arrayIndex < 0 || arrayIndex > array.Length)            {                throw new System.ArgumentOutOfRangeException(nameof(arrayIndex), "Array Index Out of Range");            }            if (array.Length - arrayIndex < Count)            {                throw new System.ArgumentException("The number of items in the SyncDictionary is greater than the available space from arrayIndex to the end of the destination array");            }            int i = arrayIndex;            foreach (KeyValuePair<TKey, TValue> item in objects)            {                array[i] = item;                i++;            }        }        public bool Remove(KeyValuePair<TKey, TValue> item)        {            bool result = objects.Remove(item.Key);            if (result)            {                AddOperation(Operation.OP_REMOVE, item.Key, item.Value);            }            return result;        }        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => objects.GetEnumerator();        IEnumerator IEnumerable.GetEnumerator() => objects.GetEnumerator();    }    public class SyncDictionary<TKey, TValue> : SyncIDictionary<TKey, TValue>    {        public SyncDictionary() : base(new Dictionary<TKey, TValue>()) {}        public SyncDictionary(IEqualityComparer<TKey> eq) : base(new Dictionary<TKey, TValue>(eq)) {}        public new Dictionary<TKey, TValue>.ValueCollection Values => ((Dictionary<TKey, TValue>)objects).Values;        public new Dictionary<TKey, TValue>.KeyCollection Keys => ((Dictionary<TKey, TValue>)objects).Keys;        public new Dictionary<TKey, TValue>.Enumerator GetEnumerator() => ((Dictionary<TKey, TValue>)objects).GetEnumerator();    }}
 |