mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-10-31 05:27:07 +01:00 
			
		
		
		
	CHANGED TO MIRROR
This commit is contained in:
		
							
								
								
									
										310
									
								
								Assets/Mirror/Runtime/SyncDictionary.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								Assets/Mirror/Runtime/SyncDictionary.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,310 @@ | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace Mirror | ||||
| { | ||||
|     public class SyncIDictionary<TKey, TValue> : SyncObject, IDictionary<TKey, TValue>, 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; | ||||
|         } | ||||
|  | ||||
|         // list of changes. | ||||
|         // -> insert/delete/clear is only ONE change | ||||
|         // -> changing the same slot 10x caues 10 changes. | ||||
|         // -> note that this grows until next sync(!) | ||||
|         // TODO Dictionary<key, change> to avoid ever growing changes / redundant changes! | ||||
|         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 override void Reset() | ||||
|         { | ||||
|             IsReadOnly = false; | ||||
|             changes.Clear(); | ||||
|             changesAhead = 0; | ||||
|             objects.Clear(); | ||||
|         } | ||||
|  | ||||
|         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 override void ClearChanges() => 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 | ||||
|             }; | ||||
|  | ||||
|             if (IsRecording()) | ||||
|             { | ||||
|                 changes.Add(change); | ||||
|                 OnDirty?.Invoke(); | ||||
|             } | ||||
|  | ||||
|             Callback?.Invoke(op, key, item); | ||||
|         } | ||||
|  | ||||
|         public override 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 override 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 override 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 override 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(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 SyncDictionary(IDictionary<TKey, TValue> d) : base(new Dictionary<TKey, TValue>(d)) {} | ||||
|         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(); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 DerTyp187
					DerTyp187