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;
}
}