The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.0.0-pre.1] - 2024-06-17 ### Added - Added event `NetworkManager.OnSessionOwnerPromoted` that is invoked when a new session owner promotion occurs. (#2948) - Added `NetworkRigidBodyBase.GetLinearVelocity` and `NetworkRigidBodyBase.SetLinearVelocity` convenience/helper methods. (#2948) - Added `NetworkRigidBodyBase.GetAngularVelocity` and `NetworkRigidBodyBase.SetAngularVelocity` convenience/helper methods. (#2948) ### Fixed - Fixed issue when `NetworkTransform` half float precision is enabled and ownership changes the current base position was not being synchronized. (#2948) - Fixed issue where `OnClientConnected` not being invoked on the session owner when connecting to a new distributed authority session. (#2948) - Fixed issue where Rigidbody micro-motion (i.e. relatively small velocities) would result in non-authority instances slightly stuttering as the body would come to a rest (i.e. no motion). Now, the threshold value can increase at higher velocities and can decrease slightly below the provided threshold to account for this. (#2948) ### Changed - Changed the client's owned objects is now returned (`NetworkClient` and `NetworkSpawnManager`) as an array as opposed to a list for performance purposes. (#2948) - Changed `NetworkTransfrom.TryCommitTransformToServer` to be internal as it will be removed by the final 2.0.0 release. (#2948) - Changed `NetworkTransformEditor.OnEnable` to a virtual method to be able to customize a `NetworkTransform` derived class by creating a derived editor control from `NetworkTransformEditor`. (#2948)
358 lines
9.9 KiB
C#
358 lines
9.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Netcode;
|
|
|
|
namespace Unity.Netcode
|
|
{
|
|
internal static class NetworkVariableEquality<T>
|
|
{
|
|
// Compares two values of the same unmanaged type by underlying memory
|
|
// Ignoring any overridden value checks
|
|
// Size is fixed
|
|
internal static unsafe bool ValueEquals<TValueType>(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<TValueType>(ref NativeList<TValueType> a, ref NativeList<TValueType> 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<TValueType>(ref NativeArray<TValueType> a, ref NativeArray<TValueType> 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<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class, IEquatable<TValueType>
|
|
{
|
|
if (a == null)
|
|
{
|
|
return b == null;
|
|
}
|
|
|
|
if (b == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return a.Equals(b);
|
|
}
|
|
|
|
internal static bool EqualityEquals<TValueType>(ref TValueType a, ref TValueType b) where TValueType : unmanaged, IEquatable<TValueType>
|
|
{
|
|
return a.Equals(b);
|
|
}
|
|
|
|
internal static bool EqualityEqualsList<TValueType>(ref List<TValueType> a, ref List<TValueType> 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<TValueType>.AreEqual(ref aItem, ref bItem))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal static bool EqualityEqualsHashSet<TValueType>(ref HashSet<TValueType> a, ref HashSet<TValueType> b) where TValueType : IEquatable<TValueType>
|
|
{
|
|
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<TValueType>(ref NativeArray<TValueType> a, ref NativeArray<TValueType> b) where TValueType : unmanaged, IEquatable<TValueType>
|
|
{
|
|
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<TValueType>(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<TValueType>(ref NativeList<TValueType> a, ref NativeList<TValueType> b) where TValueType : unmanaged, IEquatable<TValueType>
|
|
{
|
|
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<TValueType>(ref NativeHashSet<TValueType> a, ref NativeHashSet<TValueType> b) where TValueType : unmanaged, IEquatable<TValueType>
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <typeparam name="TKey">The type the associated NetworkVariable dictionary collection key templated on</typeparam>
|
|
/// <typeparam name="TVal">The type the associated NetworkVariable dictionary collection value templated on</typeparam>
|
|
internal class NetworkVariableDictionarySerialization<TKey, TVal>
|
|
where TKey : IEquatable<TKey>
|
|
{
|
|
internal static bool GenericEqualsDictionary(ref Dictionary<TKey, TVal> a, ref Dictionary<TKey, TVal> 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<TVal>.AreEqual(ref bVal, ref val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
|
|
internal class NetworkVariableMapSerialization<TKey, TVal>
|
|
where TKey : unmanaged, IEquatable<TKey>
|
|
where TVal : unmanaged
|
|
{
|
|
internal static bool GenericEqualsNativeHashMap(ref NativeHashMap<TKey, TVal> a, ref NativeHashMap<TKey, TVal> 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<TVal>.AreEqual(ref item.Value, ref val))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|