SyncDictionary.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using JetBrains.Annotations;
  4. namespace Mirror
  5. {
  6. public class SyncIDictionary<TKey, TValue> : IDictionary<TKey, TValue>, SyncObject, IReadOnlyDictionary<TKey, TValue>
  7. {
  8. public delegate void SyncDictionaryChanged(Operation op, TKey key, TValue item);
  9. protected readonly IDictionary<TKey, TValue> objects;
  10. public int Count => objects.Count;
  11. public bool IsReadOnly { get; private set; }
  12. public event SyncDictionaryChanged Callback;
  13. public enum Operation : byte
  14. {
  15. OP_ADD,
  16. OP_CLEAR,
  17. OP_REMOVE,
  18. OP_SET
  19. }
  20. struct Change
  21. {
  22. internal Operation operation;
  23. internal TKey key;
  24. internal TValue item;
  25. }
  26. readonly List<Change> changes = new List<Change>();
  27. // how many changes we need to ignore
  28. // this is needed because when we initialize the list,
  29. // we might later receive changes that have already been applied
  30. // so we need to skip them
  31. int changesAhead;
  32. public void Reset()
  33. {
  34. IsReadOnly = false;
  35. changes.Clear();
  36. changesAhead = 0;
  37. objects.Clear();
  38. }
  39. public bool IsDirty => changes.Count > 0;
  40. public ICollection<TKey> Keys => objects.Keys;
  41. public ICollection<TValue> Values => objects.Values;
  42. IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => objects.Keys;
  43. IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => objects.Values;
  44. // throw away all the changes
  45. // this should be called after a successful sync
  46. public void Flush() => changes.Clear();
  47. public SyncIDictionary(IDictionary<TKey, TValue> objects)
  48. {
  49. this.objects = objects;
  50. }
  51. void AddOperation(Operation op, TKey key, TValue item)
  52. {
  53. if (IsReadOnly)
  54. {
  55. throw new System.InvalidOperationException("SyncDictionaries can only be modified by the server");
  56. }
  57. Change change = new Change
  58. {
  59. operation = op,
  60. key = key,
  61. item = item
  62. };
  63. changes.Add(change);
  64. Callback?.Invoke(op, key, item);
  65. }
  66. public void OnSerializeAll(NetworkWriter writer)
  67. {
  68. // if init, write the full list content
  69. writer.WriteUInt((uint)objects.Count);
  70. foreach (KeyValuePair<TKey, TValue> syncItem in objects)
  71. {
  72. writer.Write(syncItem.Key);
  73. writer.Write(syncItem.Value);
  74. }
  75. // all changes have been applied already
  76. // thus the client will need to skip all the pending changes
  77. // or they would be applied again.
  78. // So we write how many changes are pending
  79. writer.WriteUInt((uint)changes.Count);
  80. }
  81. public void OnSerializeDelta(NetworkWriter writer)
  82. {
  83. // write all the queued up changes
  84. writer.WriteUInt((uint)changes.Count);
  85. for (int i = 0; i < changes.Count; i++)
  86. {
  87. Change change = changes[i];
  88. writer.WriteByte((byte)change.operation);
  89. switch (change.operation)
  90. {
  91. case Operation.OP_ADD:
  92. case Operation.OP_REMOVE:
  93. case Operation.OP_SET:
  94. writer.Write(change.key);
  95. writer.Write(change.item);
  96. break;
  97. case Operation.OP_CLEAR:
  98. break;
  99. }
  100. }
  101. }
  102. public void OnDeserializeAll(NetworkReader reader)
  103. {
  104. // This list can now only be modified by synchronization
  105. IsReadOnly = true;
  106. // if init, write the full list content
  107. int count = (int)reader.ReadUInt();
  108. objects.Clear();
  109. changes.Clear();
  110. for (int i = 0; i < count; i++)
  111. {
  112. TKey key = reader.Read<TKey>();
  113. TValue obj = reader.Read<TValue>();
  114. objects.Add(key, obj);
  115. }
  116. // We will need to skip all these changes
  117. // the next time the list is synchronized
  118. // because they have already been applied
  119. changesAhead = (int)reader.ReadUInt();
  120. }
  121. public void OnDeserializeDelta(NetworkReader reader)
  122. {
  123. // This list can now only be modified by synchronization
  124. IsReadOnly = true;
  125. int changesCount = (int)reader.ReadUInt();
  126. for (int i = 0; i < changesCount; i++)
  127. {
  128. Operation operation = (Operation)reader.ReadByte();
  129. // apply the operation only if it is a new change
  130. // that we have not applied yet
  131. bool apply = changesAhead == 0;
  132. TKey key = default;
  133. TValue item = default;
  134. switch (operation)
  135. {
  136. case Operation.OP_ADD:
  137. case Operation.OP_SET:
  138. key = reader.Read<TKey>();
  139. item = reader.Read<TValue>();
  140. if (apply)
  141. {
  142. objects[key] = item;
  143. }
  144. break;
  145. case Operation.OP_CLEAR:
  146. if (apply)
  147. {
  148. objects.Clear();
  149. }
  150. break;
  151. case Operation.OP_REMOVE:
  152. key = reader.Read<TKey>();
  153. item = reader.Read<TValue>();
  154. if (apply)
  155. {
  156. objects.Remove(key);
  157. }
  158. break;
  159. }
  160. if (apply)
  161. {
  162. Callback?.Invoke(operation, key, item);
  163. }
  164. // we just skipped this change
  165. else
  166. {
  167. changesAhead--;
  168. }
  169. }
  170. }
  171. public void Clear()
  172. {
  173. objects.Clear();
  174. AddOperation(Operation.OP_CLEAR, default, default);
  175. }
  176. public bool ContainsKey(TKey key) => objects.ContainsKey(key);
  177. public bool Remove(TKey key)
  178. {
  179. if (objects.TryGetValue(key, out TValue item) && objects.Remove(key))
  180. {
  181. AddOperation(Operation.OP_REMOVE, key, item);
  182. return true;
  183. }
  184. return false;
  185. }
  186. public TValue this[TKey i]
  187. {
  188. get => objects[i];
  189. set
  190. {
  191. if (ContainsKey(i))
  192. {
  193. objects[i] = value;
  194. AddOperation(Operation.OP_SET, i, value);
  195. }
  196. else
  197. {
  198. objects[i] = value;
  199. AddOperation(Operation.OP_ADD, i, value);
  200. }
  201. }
  202. }
  203. public bool TryGetValue(TKey key, out TValue value) => objects.TryGetValue(key, out value);
  204. public void Add(TKey key, TValue value)
  205. {
  206. objects.Add(key, value);
  207. AddOperation(Operation.OP_ADD, key, value);
  208. }
  209. public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
  210. public bool Contains(KeyValuePair<TKey, TValue> item)
  211. {
  212. return TryGetValue(item.Key, out TValue val) && EqualityComparer<TValue>.Default.Equals(val, item.Value);
  213. }
  214. public void CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
  215. {
  216. if (arrayIndex < 0 || arrayIndex > array.Length)
  217. {
  218. throw new System.ArgumentOutOfRangeException(nameof(arrayIndex), "Array Index Out of Range");
  219. }
  220. if (array.Length - arrayIndex < Count)
  221. {
  222. 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");
  223. }
  224. int i = arrayIndex;
  225. foreach (KeyValuePair<TKey, TValue> item in objects)
  226. {
  227. array[i] = item;
  228. i++;
  229. }
  230. }
  231. public bool Remove(KeyValuePair<TKey, TValue> item)
  232. {
  233. bool result = objects.Remove(item.Key);
  234. if (result)
  235. {
  236. AddOperation(Operation.OP_REMOVE, item.Key, item.Value);
  237. }
  238. return result;
  239. }
  240. public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => objects.GetEnumerator();
  241. IEnumerator IEnumerable.GetEnumerator() => objects.GetEnumerator();
  242. }
  243. public class SyncDictionary<TKey, TValue> : SyncIDictionary<TKey, TValue>
  244. {
  245. public SyncDictionary() : base(new Dictionary<TKey, TValue>()) {}
  246. public SyncDictionary(IEqualityComparer<TKey> eq) : base(new Dictionary<TKey, TValue>(eq)) {}
  247. public new Dictionary<TKey, TValue>.ValueCollection Values => ((Dictionary<TKey, TValue>)objects).Values;
  248. public new Dictionary<TKey, TValue>.KeyCollection Keys => ((Dictionary<TKey, TValue>)objects).Keys;
  249. public new Dictionary<TKey, TValue>.Enumerator GetEnumerator() => ((Dictionary<TKey, TValue>)objects).GetEnumerator();
  250. }
  251. }