using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Netcode { internal static class NetworkVariableEquality { // Compares two values of the same unmanaged type by underlying memory // Ignoring any overridden value checks // Size is fixed internal static unsafe bool ValueEquals(ref TValueType a, ref TValueType b) where TValueType : unmanaged { // get unmanaged pointers var aptr = UnsafeUtility.AddressOf(ref a); var bptr = UnsafeUtility.AddressOf(ref b); // compare addresses return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType)) == 0; } #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT // Compares two values of the same unmanaged type by underlying memory // Ignoring any overridden value checks // Size is fixed internal static unsafe bool ValueEqualsList(ref NativeList a, ref NativeList b) where TValueType : unmanaged { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } if (a.Length != b.Length) { return false; } #if UTP_TRANSPORT_2_0_ABOVE var aptr = a.GetUnsafePtr(); var bptr = b.GetUnsafePtr(); #else var aptr = (TValueType*)a.GetUnsafePtr(); var bptr = (TValueType*)b.GetUnsafePtr(); #endif return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType) * a.Length) == 0; } #endif // Compares two values of the same unmanaged type by underlying memory // Ignoring any overridden value checks // Size is fixed internal static unsafe bool ValueEqualsArray(ref NativeArray a, ref NativeArray b) where TValueType : unmanaged { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } if (a.Length != b.Length) { return false; } var aptr = (TValueType*)a.GetUnsafePtr(); var bptr = (TValueType*)b.GetUnsafePtr(); return UnsafeUtility.MemCmp(aptr, bptr, sizeof(TValueType) * a.Length) == 0; } internal static bool EqualityEqualsObject(ref TValueType a, ref TValueType b) where TValueType : class, IEquatable { if (a == null) { return b == null; } if (b == null) { return false; } return a.Equals(b); } internal static bool EqualityEquals(ref TValueType a, ref TValueType b) where TValueType : unmanaged, IEquatable { return a.Equals(b); } internal static bool EqualityEqualsList(ref List a, ref List b) { if (a == null != (b == null)) { return false; } if (a == null) { return true; } if (a.Count != b.Count) { return false; } for (var i = 0; i < a.Count; ++i) { var aItem = a[i]; var bItem = b[i]; if (!NetworkVariableSerialization.AreEqual(ref aItem, ref bItem)) { return false; } } return true; } internal static bool EqualityEqualsHashSet(ref HashSet a, ref HashSet b) where TValueType : IEquatable { if (a == null != (b == null)) { return false; } if (a == null) { return true; } if (a.Count != b.Count) { return false; } foreach (var item in a) { if (!b.Contains(item)) { return false; } } return true; } // Compares two values of the same unmanaged type by underlying memory // Ignoring any overridden value checks // Size is fixed internal static unsafe bool EqualityEqualsArray(ref NativeArray a, ref NativeArray b) where TValueType : unmanaged, IEquatable { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } if (a.Length != b.Length) { return false; } var aptr = (TValueType*)a.GetUnsafePtr(); var bptr = (TValueType*)b.GetUnsafePtr(); for (var i = 0; i < a.Length; ++i) { if (!EqualityEquals(ref aptr[i], ref bptr[i])) { return false; } } return true; } internal static bool ClassEquals(ref TValueType a, ref TValueType b) where TValueType : class { return a == b; } #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT // Compares two values of the same unmanaged type by underlying memory // Ignoring any overridden value checks // Size is fixed internal static unsafe bool EqualityEqualsNativeList(ref NativeList a, ref NativeList b) where TValueType : unmanaged, IEquatable { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } if (a.Length != b.Length) { return false; } #if UTP_TRANSPORT_2_0_ABOVE var aptr = a.GetUnsafePtr(); var bptr = b.GetUnsafePtr(); #else var aptr = (TValueType*)a.GetUnsafePtr(); var bptr = (TValueType*)b.GetUnsafePtr(); #endif for (var i = 0; i < a.Length; ++i) { if (!EqualityEquals(ref aptr[i], ref bptr[i])) { return false; } } return true; } internal static bool EqualityEqualsNativeHashSet(ref NativeHashSet a, ref NativeHashSet b) where TValueType : unmanaged, IEquatable { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } #if UTP_TRANSPORT_2_0_ABOVE if (a.Count != b.Count) #else if (a.Count() != b.Count()) #endif { return false; } foreach (var item in a) { if (!b.Contains(item)) { return false; } } return true; } #endif } } /// /// Support methods for equality of NetworkVariable collection types. /// Because there are multiple overloads of WriteValue/ReadValue based on different generic constraints, /// but there's no way to achieve the same thing with a class, this sets up various read/write schemes /// based on which constraints are met by `T` using reflection, which is done at module load time. /// /// The type the associated NetworkVariable dictionary collection key templated on /// The type the associated NetworkVariable dictionary collection value templated on internal class NetworkVariableDictionarySerialization where TKey : IEquatable { internal static bool GenericEqualsDictionary(ref Dictionary a, ref Dictionary b) { if (a == null != (b == null)) { return false; } if (a == null) { return true; } if (a.Count != b.Count) { return false; } foreach (var item in a) { var hasKey = b.TryGetValue(item.Key, out var val); if (!hasKey) { return false; } var bVal = item.Value; if (!NetworkVariableSerialization.AreEqual(ref bVal, ref val)) { return false; } } return true; } } #if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT internal class NetworkVariableMapSerialization where TKey : unmanaged, IEquatable where TVal : unmanaged { internal static bool GenericEqualsNativeHashMap(ref NativeHashMap a, ref NativeHashMap b) { if (a.IsCreated != b.IsCreated) { return false; } if (!a.IsCreated) { return true; } #if UTP_TRANSPORT_2_0_ABOVE if (a.Count != b.Count) #else if (a.Count() != b.Count()) #endif { return false; } foreach (var item in a) { var hasKey = b.TryGetValue(item.Key, out var val); if (!hasKey || !NetworkVariableSerialization.AreEqual(ref item.Value, ref val)) { return false; } } return true; } } #endif