SyncDictionary.cs 10 KB

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