SyncDictionary.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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 => !IsWritable();
  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. changes.Clear();
  39. changesAhead = 0;
  40. objects.Clear();
  41. }
  42. public ICollection<TKey> Keys => objects.Keys;
  43. public ICollection<TValue> Values => objects.Values;
  44. IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => objects.Keys;
  45. IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => objects.Values;
  46. // throw away all the changes
  47. // this should be called after a successful sync
  48. public override void ClearChanges() => changes.Clear();
  49. public SyncIDictionary(IDictionary<TKey, TValue> objects)
  50. {
  51. this.objects = objects;
  52. }
  53. void AddOperation(Operation op, TKey key, TValue item, bool checkAccess)
  54. {
  55. if (checkAccess && IsReadOnly)
  56. {
  57. throw new System.InvalidOperationException("SyncDictionaries can only be modified by the owner.");
  58. }
  59. Change change = new Change
  60. {
  61. operation = op,
  62. key = key,
  63. item = item
  64. };
  65. if (IsRecording())
  66. {
  67. changes.Add(change);
  68. OnDirty?.Invoke();
  69. }
  70. Callback?.Invoke(op, key, item);
  71. }
  72. public override void OnSerializeAll(NetworkWriter writer)
  73. {
  74. // if init, write the full list content
  75. writer.WriteUInt((uint)objects.Count);
  76. foreach (KeyValuePair<TKey, TValue> syncItem in objects)
  77. {
  78. writer.Write(syncItem.Key);
  79. writer.Write(syncItem.Value);
  80. }
  81. // all changes have been applied already
  82. // thus the client will need to skip all the pending changes
  83. // or they would be applied again.
  84. // So we write how many changes are pending
  85. writer.WriteUInt((uint)changes.Count);
  86. }
  87. public override void OnSerializeDelta(NetworkWriter writer)
  88. {
  89. // write all the queued up changes
  90. writer.WriteUInt((uint)changes.Count);
  91. for (int i = 0; i < changes.Count; i++)
  92. {
  93. Change change = changes[i];
  94. writer.WriteByte((byte)change.operation);
  95. switch (change.operation)
  96. {
  97. case Operation.OP_ADD:
  98. case Operation.OP_SET:
  99. writer.Write(change.key);
  100. writer.Write(change.item);
  101. break;
  102. case Operation.OP_REMOVE:
  103. writer.Write(change.key);
  104. break;
  105. case Operation.OP_CLEAR:
  106. break;
  107. }
  108. }
  109. }
  110. public override void OnDeserializeAll(NetworkReader reader)
  111. {
  112. // if init, write the full list content
  113. int count = (int)reader.ReadUInt();
  114. objects.Clear();
  115. changes.Clear();
  116. for (int i = 0; i < count; i++)
  117. {
  118. TKey key = reader.Read<TKey>();
  119. TValue obj = reader.Read<TValue>();
  120. objects.Add(key, obj);
  121. }
  122. // We will need to skip all these changes
  123. // the next time the list is synchronized
  124. // because they have already been applied
  125. changesAhead = (int)reader.ReadUInt();
  126. }
  127. public override void OnDeserializeDelta(NetworkReader reader)
  128. {
  129. int changesCount = (int)reader.ReadUInt();
  130. for (int i = 0; i < changesCount; i++)
  131. {
  132. Operation operation = (Operation)reader.ReadByte();
  133. // apply the operation only if it is a new change
  134. // that we have not applied yet
  135. bool apply = changesAhead == 0;
  136. TKey key = default;
  137. TValue item = default;
  138. switch (operation)
  139. {
  140. case Operation.OP_ADD:
  141. case Operation.OP_SET:
  142. key = reader.Read<TKey>();
  143. item = reader.Read<TValue>();
  144. if (apply)
  145. {
  146. // add dirty + changes.
  147. // ClientToServer needs to set dirty in server OnDeserialize.
  148. // no access check: server OnDeserialize can always
  149. // write, even for ClientToServer (for broadcasting).
  150. if (ContainsKey(key))
  151. {
  152. objects[key] = item; // assign after ContainsKey check
  153. AddOperation(Operation.OP_SET, key, item, false);
  154. }
  155. else
  156. {
  157. objects[key] = item; // assign after ContainsKey check
  158. AddOperation(Operation.OP_ADD, key, item, false);
  159. }
  160. }
  161. break;
  162. case Operation.OP_CLEAR:
  163. if (apply)
  164. {
  165. objects.Clear();
  166. // add dirty + changes.
  167. // ClientToServer needs to set dirty in server OnDeserialize.
  168. // no access check: server OnDeserialize can always
  169. // write, even for ClientToServer (for broadcasting).
  170. AddOperation(Operation.OP_CLEAR, default, default, false);
  171. }
  172. break;
  173. case Operation.OP_REMOVE:
  174. key = reader.Read<TKey>();
  175. if (apply)
  176. {
  177. if (objects.TryGetValue(key, out item))
  178. {
  179. // add dirty + changes.
  180. // ClientToServer needs to set dirty in server OnDeserialize.
  181. // no access check: server OnDeserialize can always
  182. // write, even for ClientToServer (for broadcasting).
  183. objects.Remove(key);
  184. AddOperation(Operation.OP_REMOVE, key, item, false);
  185. }
  186. }
  187. break;
  188. }
  189. if (!apply)
  190. {
  191. // we just skipped this change
  192. changesAhead--;
  193. }
  194. }
  195. }
  196. public void Clear()
  197. {
  198. objects.Clear();
  199. AddOperation(Operation.OP_CLEAR, default, default, true);
  200. }
  201. public bool ContainsKey(TKey key) => objects.ContainsKey(key);
  202. public bool Remove(TKey key)
  203. {
  204. if (objects.TryGetValue(key, out TValue item) && objects.Remove(key))
  205. {
  206. AddOperation(Operation.OP_REMOVE, key, item, true);
  207. return true;
  208. }
  209. return false;
  210. }
  211. public TValue this[TKey i]
  212. {
  213. get => objects[i];
  214. set
  215. {
  216. if (ContainsKey(i))
  217. {
  218. objects[i] = value;
  219. AddOperation(Operation.OP_SET, i, value, true);
  220. }
  221. else
  222. {
  223. objects[i] = value;
  224. AddOperation(Operation.OP_ADD, i, value, true);
  225. }
  226. }
  227. }
  228. public bool TryGetValue(TKey key, out TValue value) => objects.TryGetValue(key, out value);
  229. public void Add(TKey key, TValue value)
  230. {
  231. objects.Add(key, value);
  232. AddOperation(Operation.OP_ADD, key, value, true);
  233. }
  234. public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
  235. public bool Contains(KeyValuePair<TKey, TValue> item)
  236. {
  237. return TryGetValue(item.Key, out TValue val) && EqualityComparer<TValue>.Default.Equals(val, item.Value);
  238. }
  239. public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
  240. {
  241. if (arrayIndex < 0 || arrayIndex > array.Length)
  242. {
  243. throw new System.ArgumentOutOfRangeException(nameof(arrayIndex), "Array Index Out of Range");
  244. }
  245. if (array.Length - arrayIndex < Count)
  246. {
  247. 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");
  248. }
  249. int i = arrayIndex;
  250. foreach (KeyValuePair<TKey, TValue> item in objects)
  251. {
  252. array[i] = item;
  253. i++;
  254. }
  255. }
  256. public bool Remove(KeyValuePair<TKey, TValue> item)
  257. {
  258. bool result = objects.Remove(item.Key);
  259. if (result)
  260. {
  261. AddOperation(Operation.OP_REMOVE, item.Key, item.Value, true);
  262. }
  263. return result;
  264. }
  265. public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => objects.GetEnumerator();
  266. IEnumerator IEnumerable.GetEnumerator() => objects.GetEnumerator();
  267. }
  268. public class SyncDictionary<TKey, TValue> : SyncIDictionary<TKey, TValue>
  269. {
  270. public SyncDictionary() : base(new Dictionary<TKey, TValue>()) {}
  271. public SyncDictionary(IEqualityComparer<TKey> eq) : base(new Dictionary<TKey, TValue>(eq)) {}
  272. public SyncDictionary(IDictionary<TKey, TValue> d) : base(new Dictionary<TKey, TValue>(d)) {}
  273. public new Dictionary<TKey, TValue>.ValueCollection Values => ((Dictionary<TKey, TValue>)objects).Values;
  274. public new Dictionary<TKey, TValue>.KeyCollection Keys => ((Dictionary<TKey, TValue>)objects).Keys;
  275. public new Dictionary<TKey, TValue>.Enumerator GetEnumerator() => ((Dictionary<TKey, TValue>)objects).GetEnumerator();
  276. }
  277. }