using System.Collections; using System.Collections.Generic; using System.IO; using MLAPI.Serialization.Pooled; using MLAPI.Transports; namespace MLAPI.NetworkVariable.Collections { /// /// Event based NetworkVariable container for syncing Dictionaries /// /// The type for the dictionary keys /// The type for the dictionary values public class NetworkDictionary : IDictionary, INetworkVariable { /// /// Gets the last time the variable was synced /// public float LastSyncedTime { get; internal set; } /// /// The settings for this container /// public readonly NetworkVariableSettings Settings = new NetworkVariableSettings(); private readonly IDictionary m_Dictionary = new Dictionary(); private NetworkBehaviour m_NetworkBehaviour; private readonly List> m_DirtyEvents = new List>(); /// /// Delegate type for dictionary changed event /// /// Struct containing information about the change event public delegate void OnDictionaryChangedDelegate(NetworkDictionaryEvent changeEvent); /// /// The callback to be invoked when the dictionary gets changed /// public event OnDictionaryChangedDelegate OnDictionaryChanged; /// /// Creates a NetworkDictionary with the default value and settings /// public NetworkDictionary() { } /// /// Creates a NetworkDictionary with the default value and custom settings /// /// The settings to use for the NetworkDictionary public NetworkDictionary(NetworkVariableSettings settings) { Settings = settings; } /// /// Creates a NetworkDictionary with a custom value and custom settings /// /// The settings to use for the NetworkDictionary /// The initial value to use for the NetworkDictionary public NetworkDictionary(NetworkVariableSettings settings, IDictionary value) { Settings = settings; m_Dictionary = value; } /// /// Creates a NetworkDictionary with a custom value and the default settings /// /// The initial value to use for the NetworkDictionary public NetworkDictionary(IDictionary value) { m_Dictionary = value; } /// public void ResetDirty() { m_DirtyEvents.Clear(); LastSyncedTime = NetworkManager.Singleton.NetworkTime; } /// public NetworkChannel GetChannel() { return Settings.SendNetworkChannel; } /// public void ReadDelta(Stream stream, bool keepDirtyDelta, ushort localTick, ushort remoteTick) { using (var reader = PooledNetworkReader.Get(stream)) { ushort deltaCount = reader.ReadUInt16Packed(); for (int i = 0; i < deltaCount; i++) { NetworkDictionaryEvent.EventType eventType = (NetworkDictionaryEvent.EventType)reader.ReadBits(3); switch (eventType) { case NetworkDictionaryEvent.EventType.Add: { TKey key = (TKey)reader.ReadObjectPacked(typeof(TKey)); TValue value = (TValue)reader.ReadObjectPacked(typeof(TValue)); m_Dictionary.Add(key, value); if (OnDictionaryChanged != null) { OnDictionaryChanged(new NetworkDictionaryEvent { Type = eventType, Key = key, Value = value }); } if (keepDirtyDelta) { m_DirtyEvents.Add(new NetworkDictionaryEvent() { Type = eventType, Key = key, Value = value }); } } break; case NetworkDictionaryEvent.EventType.Remove: { TKey key = (TKey)reader.ReadObjectPacked(typeof(TKey)); TValue value; m_Dictionary.TryGetValue(key, out value); m_Dictionary.Remove(key); if (OnDictionaryChanged != null) { OnDictionaryChanged(new NetworkDictionaryEvent { Type = eventType, Key = key, Value = value }); } if (keepDirtyDelta) { m_DirtyEvents.Add(new NetworkDictionaryEvent() { Type = eventType, Key = key, Value = value }); } } break; case NetworkDictionaryEvent.EventType.RemovePair: { TKey key = (TKey)reader.ReadObjectPacked(typeof(TKey)); TValue value = (TValue)reader.ReadObjectPacked(typeof(TValue)); m_Dictionary.Remove(new KeyValuePair(key, value)); if (OnDictionaryChanged != null) { OnDictionaryChanged(new NetworkDictionaryEvent { Type = eventType, Key = key, Value = value }); } if (keepDirtyDelta) { m_DirtyEvents.Add(new NetworkDictionaryEvent() { Type = eventType, Key = key, Value = value }); } } break; case NetworkDictionaryEvent.EventType.Clear: { //read nothing m_Dictionary.Clear(); if (OnDictionaryChanged != null) { OnDictionaryChanged(new NetworkDictionaryEvent { Type = eventType }); } if (keepDirtyDelta) { m_DirtyEvents.Add(new NetworkDictionaryEvent { Type = eventType }); } } break; case NetworkDictionaryEvent.EventType.Value: { TKey key = (TKey)reader.ReadObjectPacked(typeof(TKey)); TValue value = (TValue)reader.ReadObjectPacked(typeof(TValue)); m_Dictionary[key] = value; if (OnDictionaryChanged != null) { OnDictionaryChanged(new NetworkDictionaryEvent { Type = eventType, Key = key, Value = value }); } if (keepDirtyDelta) { m_DirtyEvents.Add(new NetworkDictionaryEvent() { Type = eventType, Key = key, Value = value }); } } break; } } } } /// public void ReadField(Stream stream, ushort localTick, ushort remoteTick) { using (var reader = PooledNetworkReader.Get(stream)) { m_Dictionary.Clear(); ushort entryCount = reader.ReadUInt16Packed(); for (int i = 0; i < entryCount; i++) { TKey key = (TKey)reader.ReadObjectPacked(typeof(TKey)); TValue value = (TValue)reader.ReadObjectPacked(typeof(TValue)); m_Dictionary.Add(key, value); } } } /// public void SetNetworkBehaviour(NetworkBehaviour behaviour) { m_NetworkBehaviour = behaviour; } /// public bool TryGetValue(TKey key, out TValue value) { return m_Dictionary.TryGetValue(key, out value); } /// public void WriteDelta(Stream stream) { using (var writer = PooledNetworkWriter.Get(stream)) { writer.WriteUInt16Packed((ushort)m_DirtyEvents.Count); for (int i = 0; i < m_DirtyEvents.Count; i++) { writer.WriteBits((byte)m_DirtyEvents[i].Type, 3); switch (m_DirtyEvents[i].Type) { case NetworkDictionaryEvent.EventType.Add: { writer.WriteObjectPacked(m_DirtyEvents[i].Key); writer.WriteObjectPacked(m_DirtyEvents[i].Value); } break; case NetworkDictionaryEvent.EventType.Remove: { writer.WriteObjectPacked(m_DirtyEvents[i].Key); } break; case NetworkDictionaryEvent.EventType.RemovePair: { writer.WriteObjectPacked(m_DirtyEvents[i].Key); writer.WriteObjectPacked(m_DirtyEvents[i].Value); } break; case NetworkDictionaryEvent.EventType.Clear: { //write nothing } break; case NetworkDictionaryEvent.EventType.Value: { writer.WriteObjectPacked(m_DirtyEvents[i].Key); writer.WriteObjectPacked(m_DirtyEvents[i].Value); } break; } } } } /// public void WriteField(Stream stream) { using (var writer = PooledNetworkWriter.Get(stream)) { writer.WriteUInt16Packed((ushort)m_Dictionary.Count); foreach (KeyValuePair pair in m_Dictionary) { writer.WriteObjectPacked(pair.Key); writer.WriteObjectPacked(pair.Value); } } } /// public bool CanClientWrite(ulong clientId) { switch (Settings.WritePermission) { case NetworkVariablePermission.Everyone: return true; case NetworkVariablePermission.ServerOnly: return false; case NetworkVariablePermission.OwnerOnly: return m_NetworkBehaviour.OwnerClientId == clientId; case NetworkVariablePermission.Custom: { if (Settings.WritePermissionCallback == null) return false; return Settings.WritePermissionCallback(clientId); } } return true; } /// public bool CanClientRead(ulong clientId) { switch (Settings.ReadPermission) { case NetworkVariablePermission.Everyone: return true; case NetworkVariablePermission.ServerOnly: return false; case NetworkVariablePermission.OwnerOnly: return m_NetworkBehaviour.OwnerClientId == clientId; case NetworkVariablePermission.Custom: { if (Settings.ReadPermissionCallback == null) return false; return Settings.ReadPermissionCallback(clientId); } } return true; } /// public bool IsDirty() { if (m_DirtyEvents.Count == 0) return false; if (Settings.SendTickrate == 0) return true; if (Settings.SendTickrate < 0) return false; if (NetworkManager.Singleton.NetworkTime - LastSyncedTime >= (1f / Settings.SendTickrate)) return true; return false; } /// public TValue this[TKey key] { get => m_Dictionary[key]; set { if (NetworkManager.Singleton.IsServer) m_Dictionary[key] = value; NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.Value, Key = key, Value = value }; HandleAddDictionaryEvent(dictionaryEvent); } } /// public ICollection Keys => m_Dictionary.Keys; /// public ICollection Values => m_Dictionary.Values; /// public int Count => m_Dictionary.Count; /// public bool IsReadOnly => m_Dictionary.IsReadOnly; /// public void Add(TKey key, TValue value) { if (NetworkManager.Singleton.IsServer) m_Dictionary.Add(key, value); NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.Add, Key = key, Value = value }; HandleAddDictionaryEvent(dictionaryEvent); } /// public void Add(KeyValuePair item) { if (NetworkManager.Singleton.IsServer) m_Dictionary.Add(item); NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.Add, Key = item.Key, Value = item.Value }; HandleAddDictionaryEvent(dictionaryEvent); } /// public void Clear() { if (NetworkManager.Singleton.IsServer) m_Dictionary.Clear(); NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.Clear }; HandleAddDictionaryEvent(dictionaryEvent); } /// public bool Contains(KeyValuePair item) { return m_Dictionary.Contains(item); } /// public bool ContainsKey(TKey key) { return m_Dictionary.ContainsKey(key); } /// public void CopyTo(KeyValuePair[] array, int arrayIndex) { m_Dictionary.CopyTo(array, arrayIndex); } /// public IEnumerator> GetEnumerator() { return m_Dictionary.GetEnumerator(); } /// public bool Remove(TKey key) { if (NetworkManager.Singleton.IsServer) m_Dictionary.Remove(key); TValue value; m_Dictionary.TryGetValue(key, out value); NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.Remove, Key = key, Value = value }; HandleAddDictionaryEvent(dictionaryEvent); return true; } /// public bool Remove(KeyValuePair item) { if (NetworkManager.Singleton.IsServer) m_Dictionary.Remove(item); NetworkDictionaryEvent dictionaryEvent = new NetworkDictionaryEvent() { Type = NetworkDictionaryEvent.EventType.RemovePair, Key = item.Key, Value = item.Value }; HandleAddDictionaryEvent(dictionaryEvent); return true; } /// IEnumerator IEnumerable.GetEnumerator() { return m_Dictionary.GetEnumerator(); } private void HandleAddDictionaryEvent(NetworkDictionaryEvent dictionaryEvent) { if (NetworkManager.Singleton.IsServer) { if (NetworkManager.Singleton.ConnectedClients.Count > 0) { m_DirtyEvents.Add(dictionaryEvent); } OnDictionaryChanged?.Invoke(dictionaryEvent); } else { m_DirtyEvents.Add(dictionaryEvent); } } public ushort RemoteTick { get { // todo: implement proper network tick for NetworkDictionary return NetworkTickSystem.NoTick; } } } /// /// Struct containing event information about changes to a NetworkDictionary. /// /// The type for the dictionary key that the event is about /// The type for the dictionary value that the event is about public struct NetworkDictionaryEvent { /// /// Enum representing the different operations available for triggering an event. /// public enum EventType { /// /// Add /// Add, /// /// Remove /// Remove, /// /// Remove pair /// RemovePair, /// /// Clear /// Clear, /// /// Value changed /// Value } /// /// Enum representing the operation made to the dictionary. /// public EventType Type; /// /// the key changed, added or removed if available. /// public TKey Key; /// /// The value changed, added or removed if available. /// public TValue Value; } }