mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-10-31 21:47:07 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			311 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			311 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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();
 | |
|     }
 | |
| }
 | 
