com.unity.netcode.gameobjects@1.1.0
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). ## [1.1.0] - 2022-10-21 ### Added - Added `NetworkManager.IsApproved` flag that is set to `true` a client has been approved.(#2261) - `UnityTransport` now provides a way to set the Relay server data directly from the `RelayServerData` structure (provided by the Unity Transport package) throuh its `SetRelayServerData` method. This allows making use of the new APIs in UTP 1.3 that simplify integration of the Relay SDK. (#2235) - IPv6 is now supported for direct connections when using `UnityTransport`. (#2232) - Added WebSocket support when using UTP 2.0 with `UseWebSockets` property in the `UnityTransport` component of the `NetworkManager` allowing to pick WebSockets for communication. When building for WebGL, this selection happens automatically. (#2201) - Added position, rotation, and scale to the `ParentSyncMessage` which provides users the ability to specify the final values on the server-side when `OnNetworkObjectParentChanged` is invoked just before the message is created (when the `Transform` values are applied to the message). (#2146) - Added `NetworkObject.TryRemoveParent` method for convenience purposes opposed to having to cast null to either `GameObject` or `NetworkObject`. (#2146) ### Changed - Updated `UnityTransport` dependency on `com.unity.transport` to 1.3.0. (#2231) - The send queues of `UnityTransport` are now dynamically-sized. This means that there shouldn't be any need anymore to tweak the 'Max Send Queue Size' value. In fact, this field is now removed from the inspector and will not be serialized anymore. It is still possible to set it manually using the `MaxSendQueueSize` property, but it is not recommended to do so aside from some specific needs (e.g. limiting the amount of memory used by the send queues in very constrained environments). (#2212) - As a consequence of the above change, the `UnityTransport.InitialMaxSendQueueSize` field is now deprecated. There is no default value anymore since send queues are dynamically-sized. (#2212) - The debug simulator in `UnityTransport` is now non-deterministic. Its random number generator used to be seeded with a constant value, leading to the same pattern of packet drops, delays, and jitter in every run. (#2196) - `NetworkVariable<>` now supports managed `INetworkSerializable` types, as well as other managed types with serialization/deserialization delegates registered to `UserNetworkVariableSerialization<T>.WriteValue` and `UserNetworkVariableSerialization<T>.ReadValue` (#2219) - `NetworkVariable<>` and `BufferSerializer<BufferSerializerReader>` now deserialize `INetworkSerializable` types in-place, rather than constructing new ones. (#2219) ### Fixed - Fixed `NetworkManager.ApprovalTimeout` will not timeout due to slower client synchronization times as it now uses the added `NetworkManager.IsApproved` flag to determined if the client has been approved or not.(#2261) - Fixed issue caused when changing ownership of objects hidden to some clients (#2242) - Fixed issue where an in-scene placed NetworkObject would not invoke NetworkBehaviour.OnNetworkSpawn if the GameObject was disabled when it was despawned. (#2239) - Fixed issue where clients were not rebuilding the `NetworkConfig` hash value for each unique connection request. (#2226) - Fixed the issue where player objects were not taking the `DontDestroyWithOwner` property into consideration when a client disconnected. (#2225) - Fixed issue where `SceneEventProgress` would not complete if a client late joins while it is still in progress. (#2222) - Fixed issue where `SceneEventProgress` would not complete if a client disconnects. (#2222) - Fixed issues with detecting if a `SceneEventProgress` has timed out. (#2222) - Fixed issue #1924 where `UnityTransport` would fail to restart after a first failure (even if what caused the initial failure was addressed). (#2220) - Fixed issue where `NetworkTransform.SetStateServerRpc` and `NetworkTransform.SetStateClientRpc` were not honoring local vs world space settings when applying the position and rotation. (#2203) - Fixed ILPP `TypeLoadException` on WebGL on MacOS Editor and potentially other platforms. (#2199) - Implicit conversion of NetworkObjectReference to GameObject will now return null instead of throwing an exception if the referenced object could not be found (i.e., was already despawned) (#2158) - Fixed warning resulting from a stray NetworkAnimator.meta file (#2153) - Fixed Connection Approval Timeout not working client side. (#2164) - Fixed issue where the `WorldPositionStays` parenting parameter was not being synchronized with clients. (#2146) - Fixed issue where parented in-scene placed `NetworkObject`s would fail for late joining clients. (#2146) - Fixed issue where scale was not being synchronized which caused issues with nested parenting and scale when `WorldPositionStays` was true. (#2146) - Fixed issue with `NetworkTransform.ApplyTransformToNetworkStateWithInfo` where it was not honoring axis sync settings when `NetworkTransformState.IsTeleportingNextFrame` was true. (#2146) - Fixed issue with `NetworkTransform.TryCommitTransformToServer` where it was not honoring the `InLocalSpace` setting. (#2146) - Fixed ClientRpcs always reporting in the profiler view as going to all clients, even when limited to a subset of clients by `ClientRpcParams`. (#2144) - Fixed RPC codegen failing to choose the correct extension methods for `FastBufferReader` and `FastBufferWriter` when the parameters were a generic type (i.e., List<int>) and extensions for multiple instantiations of that type have been defined (i.e., List<int> and List<string>) (#2142) - Fixed the issue where running a server (i.e. not host) the second player would not receive updates (unless a third player joined). (#2127) - Fixed issue where late-joining client transition synchronization could fail when more than one transition was occurring.(#2127) - Fixed throwing an exception in `OnNetworkUpdate` causing other `OnNetworkUpdate` calls to not be executed. (#1739) - Fixed synchronization when Time.timeScale is set to 0. This changes timing update to use unscaled deltatime. Now network updates rate are independent from the local time scale. (#2171) - Fixed not sending all NetworkVariables to all clients when a client connects to a server. (#1987) - Fixed IsOwner/IsOwnedByServer being wrong on the server after calling RemoveOwnership (#2211)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
@@ -11,7 +12,6 @@ namespace Unity.Netcode
|
||||
public class NetworkList<T> : NetworkVariableBase where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
private NativeList<T> m_List = new NativeList<T>(64, Allocator.Persistent);
|
||||
private NativeList<T> m_ListAtLastReset = new NativeList<T>(64, Allocator.Persistent);
|
||||
private NativeList<NetworkListEvent<T>> m_DirtyEvents = new NativeList<NetworkListEvent<T>>(64, Allocator.Persistent);
|
||||
|
||||
/// <summary>
|
||||
@@ -39,9 +39,13 @@ namespace Unity.Netcode
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
: base(readPerm, writePerm)
|
||||
{
|
||||
foreach (var value in values)
|
||||
// allow null IEnumerable<T> to mean "no values"
|
||||
if (values != null)
|
||||
{
|
||||
m_List.Add(value);
|
||||
foreach (var value in values)
|
||||
{
|
||||
m_List.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +56,6 @@ namespace Unity.Netcode
|
||||
if (m_DirtyEvents.Length > 0)
|
||||
{
|
||||
m_DirtyEvents.Clear();
|
||||
m_ListAtLastReset.CopyFrom(m_List);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +68,13 @@ namespace Unity.Netcode
|
||||
|
||||
internal void MarkNetworkObjectDirty()
|
||||
{
|
||||
if (m_NetworkBehaviour == null)
|
||||
{
|
||||
Debug.LogWarning($"NetworkList is written to, but doesn't know its NetworkBehaviour yet. " +
|
||||
"Are you modifying a NetworkList before the NetworkObject is spawned?");
|
||||
return;
|
||||
}
|
||||
|
||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
||||
}
|
||||
|
||||
@@ -127,26 +137,10 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public override void WriteField(FastBufferWriter writer)
|
||||
{
|
||||
// The listAtLastReset mechanism was put in place to deal with duplicate adds
|
||||
// upon initial spawn. However, it causes issues with in-scene placed objects
|
||||
// due to difference in spawn order. In order to address this, we pick the right
|
||||
// list based on the type of object.
|
||||
bool isSceneObject = m_NetworkBehaviour.NetworkObject.IsSceneObject != false;
|
||||
if (isSceneObject)
|
||||
writer.WriteValueSafe((ushort)m_List.Length);
|
||||
for (int i = 0; i < m_List.Length; i++)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)m_ListAtLastReset.Length);
|
||||
for (int i = 0; i < m_ListAtLastReset.Length; i++)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_ListAtLastReset.ElementAt(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe((ushort)m_List.Length);
|
||||
for (int i = 0; i < m_List.Length; i++)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_List.ElementAt(i));
|
||||
}
|
||||
NetworkVariableSerialization<T>.Write(writer, ref m_List.ElementAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +151,8 @@ namespace Unity.Netcode
|
||||
reader.ReadValueSafe(out ushort count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
var value = new T();
|
||||
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||
m_List.Add(value);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +168,8 @@ namespace Unity.Netcode
|
||||
{
|
||||
case NetworkListEvent<T>.EventType.Add:
|
||||
{
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
var value = new T();
|
||||
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||
m_List.Add(value);
|
||||
|
||||
if (OnListChanged != null)
|
||||
@@ -201,7 +197,8 @@ namespace Unity.Netcode
|
||||
case NetworkListEvent<T>.EventType.Insert:
|
||||
{
|
||||
reader.ReadValueSafe(out int index);
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
var value = new T();
|
||||
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||
|
||||
if (index < m_List.Length)
|
||||
{
|
||||
@@ -237,7 +234,8 @@ namespace Unity.Netcode
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.Remove:
|
||||
{
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
var value = new T();
|
||||
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||
int index = m_List.IndexOf(value);
|
||||
if (index == -1)
|
||||
{
|
||||
@@ -299,7 +297,8 @@ namespace Unity.Netcode
|
||||
case NetworkListEvent<T>.EventType.Value:
|
||||
{
|
||||
reader.ReadValueSafe(out int index);
|
||||
NetworkVariableSerialization<T>.Read(reader, out T value);
|
||||
var value = new T();
|
||||
NetworkVariableSerialization<T>.Read(reader, ref value);
|
||||
if (index >= m_List.Length)
|
||||
{
|
||||
throw new Exception("Shouldn't be here, index is higher than list length");
|
||||
@@ -374,6 +373,12 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public void Add(T item)
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
m_List.Add(item);
|
||||
|
||||
var listEvent = new NetworkListEvent<T>()
|
||||
@@ -389,6 +394,12 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
m_List.Clear();
|
||||
|
||||
var listEvent = new NetworkListEvent<T>()
|
||||
@@ -409,6 +420,12 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public bool Remove(T item)
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
int index = NativeArrayExtensions.IndexOf(m_List, item);
|
||||
if (index == -1)
|
||||
{
|
||||
@@ -438,6 +455,12 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
if (index < m_List.Length)
|
||||
{
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
@@ -461,6 +484,12 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
m_List.RemoveAt(index);
|
||||
|
||||
var listEvent = new NetworkListEvent<T>()
|
||||
@@ -478,6 +507,12 @@ namespace Unity.Netcode
|
||||
get => m_List[index];
|
||||
set
|
||||
{
|
||||
// check write permissions
|
||||
if (!CanClientWrite(m_NetworkBehaviour.NetworkManager.LocalClientId))
|
||||
{
|
||||
throw new InvalidOperationException("Client is not allowed to write to this NetworkList");
|
||||
}
|
||||
|
||||
var previousValue = m_List[index];
|
||||
m_List[index] = value;
|
||||
|
||||
@@ -520,7 +555,6 @@ namespace Unity.Netcode
|
||||
public override void Dispose()
|
||||
{
|
||||
m_List.Dispose();
|
||||
m_ListAtLastReset.Dispose();
|
||||
m_DirtyEvents.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
@@ -10,7 +8,7 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
|
||||
[Serializable]
|
||||
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
||||
public class NetworkVariable<T> : NetworkVariableBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate type for value changed event
|
||||
@@ -52,7 +50,7 @@ namespace Unity.Netcode
|
||||
set
|
||||
{
|
||||
// Compare bitwise
|
||||
if (ValueEquals(ref m_InternalValue, ref value))
|
||||
if (NetworkVariableSerialization<T>.AreEqual(ref m_InternalValue, ref value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -66,20 +64,6 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
// Compares two values of the same unmanaged type by underlying memory
|
||||
// Ignoring any overridden value checks
|
||||
// Size is fixed
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe bool ValueEquals(ref T a, ref T b)
|
||||
{
|
||||
// get unmanaged pointers
|
||||
var aptr = UnsafeUtility.AddressOf(ref a);
|
||||
var bptr = UnsafeUtility.AddressOf(ref b);
|
||||
|
||||
// compare addresses
|
||||
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(T)) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="Value"/>, marks the <see cref="NetworkVariable{T}"/> dirty, and invokes the <see cref="OnValueChanged"/> callback
|
||||
/// if there are subscribers to that event.
|
||||
@@ -115,7 +99,7 @@ namespace Unity.Netcode
|
||||
// would be stored in different fields
|
||||
|
||||
T previousValue = m_InternalValue;
|
||||
NetworkVariableSerialization<T>.Read(reader, out m_InternalValue);
|
||||
NetworkVariableSerialization<T>.Read(reader, ref m_InternalValue);
|
||||
|
||||
if (keepDirtyDelta)
|
||||
{
|
||||
@@ -128,7 +112,7 @@ namespace Unity.Netcode
|
||||
/// <inheritdoc />
|
||||
public override void ReadField(FastBufferReader reader)
|
||||
{
|
||||
NetworkVariableSerialization<T>.Read(reader, out m_InternalValue);
|
||||
NetworkVariableSerialization<T>.Read(reader, ref m_InternalValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
@@ -79,8 +80,15 @@ namespace Unity.Netcode
|
||||
public virtual void SetDirty(bool isDirty)
|
||||
{
|
||||
m_IsDirty = isDirty;
|
||||
if (m_IsDirty && m_NetworkBehaviour != null)
|
||||
|
||||
if (m_IsDirty)
|
||||
{
|
||||
if (m_NetworkBehaviour == null)
|
||||
{
|
||||
Debug.LogWarning($"NetworkVariable is written to, but doesn't know its NetworkBehaviour yet. " +
|
||||
"Are you modifying a NetworkVariable before the NetworkObject is spawned?");
|
||||
return;
|
||||
}
|
||||
m_NetworkBehaviour.NetworkManager.MarkNetworkObjectDirty(m_NetworkBehaviour.NetworkObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
@@ -18,7 +17,7 @@ namespace Unity.Netcode
|
||||
// Taking T as an in parameter like we do in other places would require making a copy
|
||||
// of it to pass it as a ref parameter.
|
||||
public void Write(FastBufferWriter writer, ref T value);
|
||||
public void Read(FastBufferReader reader, out T value);
|
||||
public void Read(FastBufferReader reader, ref T value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,79 +34,80 @@ namespace Unity.Netcode
|
||||
{
|
||||
writer.WriteUnmanagedSafe(value);
|
||||
}
|
||||
public void Read(FastBufferReader reader, out T value)
|
||||
public void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
reader.ReadUnmanagedSafe(out value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializer for FixedStrings, which does the same thing FastBufferWriter/FastBufferReader do,
|
||||
/// but is implemented to get the data it needs using open instance delegates that are passed in
|
||||
/// via reflection. This prevents needing T to meet any interface requirements (which isn't achievable
|
||||
/// without incurring GC allocs on every call to Write or Read - reflection + Open Instance Delegates
|
||||
/// circumvent that.)
|
||||
///
|
||||
/// Tests show that calling these delegates doesn't cause any GC allocations even though they're
|
||||
/// obtained via reflection and Delegate.CreateDelegate() and called on types that, at compile time,
|
||||
/// aren't known to actually contain those methods.
|
||||
/// Serializer for FixedStrings
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class FixedStringSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged
|
||||
internal class FixedStringSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
{
|
||||
internal delegate int GetLengthDelegate(ref T value);
|
||||
internal delegate void SetLengthDelegate(ref T value, int length);
|
||||
internal unsafe delegate byte* GetUnsafePtrDelegate(ref T value);
|
||||
|
||||
internal GetLengthDelegate GetLength;
|
||||
internal SetLengthDelegate SetLength;
|
||||
internal GetUnsafePtrDelegate GetUnsafePtr;
|
||||
|
||||
public unsafe void Write(FastBufferWriter writer, ref T value)
|
||||
public void Write(FastBufferWriter writer, ref T value)
|
||||
{
|
||||
int length = GetLength(ref value);
|
||||
byte* data = GetUnsafePtr(ref value);
|
||||
writer.WriteUnmanagedSafe(length);
|
||||
writer.WriteBytesSafe(data, length);
|
||||
writer.WriteValueSafe(value);
|
||||
}
|
||||
public unsafe void Read(FastBufferReader reader, out T value)
|
||||
public void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
value = new T();
|
||||
reader.ReadValueSafe(out int length);
|
||||
SetLength(ref value, length);
|
||||
reader.ReadBytesSafe(GetUnsafePtr(ref value), length);
|
||||
reader.ReadValueSafeInPlace(ref value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializer for INetworkSerializable types, which does the same thing
|
||||
/// FastBufferWriter/FastBufferReader do, but is implemented to call the NetworkSerialize() method
|
||||
/// via open instance delegates passed in via reflection. This prevents needing T to meet any interface
|
||||
/// requirements (which isn't achievable without incurring GC allocs on every call to Write or Read -
|
||||
/// reflection + Open Instance Delegates circumvent that.)
|
||||
///
|
||||
/// Tests show that calling these delegates doesn't cause any GC allocations even though they're
|
||||
/// obtained via reflection and Delegate.CreateDelegate() and called on types that, at compile time,
|
||||
/// aren't known to actually contain those methods.
|
||||
/// Serializer for unmanaged INetworkSerializable types
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class NetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged
|
||||
internal class UnmanagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : unmanaged, INetworkSerializable
|
||||
{
|
||||
internal delegate void WriteValueDelegate(ref T value, BufferSerializer<BufferSerializerWriter> serializer);
|
||||
internal delegate void ReadValueDelegate(ref T value, BufferSerializer<BufferSerializerReader> serializer);
|
||||
|
||||
internal WriteValueDelegate WriteValue;
|
||||
internal ReadValueDelegate ReadValue;
|
||||
public void Write(FastBufferWriter writer, ref T value)
|
||||
{
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
|
||||
WriteValue(ref value, bufferSerializer);
|
||||
value.NetworkSerialize(bufferSerializer);
|
||||
}
|
||||
public void Read(FastBufferReader reader, out T value)
|
||||
public void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
value = new T();
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
||||
ReadValue(ref value, bufferSerializer);
|
||||
value.NetworkSerialize(bufferSerializer);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializer for managed INetworkSerializable types, which differs from the unmanaged implementation in that it
|
||||
/// has to be null-aware
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class ManagedNetworkSerializableSerializer<T> : INetworkVariableSerializer<T> where T : class, INetworkSerializable, new()
|
||||
{
|
||||
public void Write(FastBufferWriter writer, ref T value)
|
||||
{
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
|
||||
bool isNull = (value == null);
|
||||
bufferSerializer.SerializeValue(ref isNull);
|
||||
if (!isNull)
|
||||
{
|
||||
value.NetworkSerialize(bufferSerializer);
|
||||
}
|
||||
}
|
||||
public void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
||||
bool isNull = false;
|
||||
bufferSerializer.SerializeValue(ref isNull);
|
||||
if (isNull)
|
||||
{
|
||||
value = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
value = new T();
|
||||
}
|
||||
value.NetworkSerialize(bufferSerializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace Unity.Netcode
|
||||
}
|
||||
UserNetworkVariableSerialization<T>.WriteValue(writer, value);
|
||||
}
|
||||
public void Read(FastBufferReader reader, out T value)
|
||||
public void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
if (UserNetworkVariableSerialization<T>.ReadValue == null || UserNetworkVariableSerialization<T>.WriteValue == null)
|
||||
{
|
||||
@@ -174,34 +174,95 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
internal static class NetworkVariableSerializationTypes
|
||||
/// <summary>
|
||||
/// This class contains initialization functions for various different types used in NetworkVariables.
|
||||
/// Generally speaking, these methods are called by a module initializer created by codegen (NetworkBehaviourILPP)
|
||||
/// and do not need to be called manually.
|
||||
///
|
||||
/// There are two types of initializers: Serializers and EqualityCheckers. Every type must have an EqualityChecker
|
||||
/// registered to it in order to be used in NetworkVariable; however, not all types need a Serializer. Types without
|
||||
/// a serializer registered will fall back to using the delegates in <see cref="UserNetworkVariableSerialization{T}"/>.
|
||||
/// If no such delegate has been registered, a type without a serializer will throw an exception on the first attempt
|
||||
/// to serialize or deserialize it. (Again, however, codegen handles this automatically and this registration doesn't
|
||||
/// typically need to be performed manually.)
|
||||
/// </summary>
|
||||
public static class NetworkVariableSerializationTypes
|
||||
{
|
||||
internal static readonly HashSet<Type> BaseSupportedTypes = new HashSet<Type>
|
||||
/// <summary>
|
||||
/// Registeres an unmanaged type that will be serialized by a direct memcpy into a buffer
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeSerializer_UnmanagedByMemcpy<T>() where T : unmanaged
|
||||
{
|
||||
typeof(bool),
|
||||
typeof(byte),
|
||||
typeof(sbyte),
|
||||
typeof(char),
|
||||
typeof(decimal),
|
||||
typeof(double),
|
||||
typeof(float),
|
||||
typeof(int),
|
||||
typeof(uint),
|
||||
typeof(long),
|
||||
typeof(ulong),
|
||||
typeof(short),
|
||||
typeof(ushort),
|
||||
typeof(Vector2),
|
||||
typeof(Vector3),
|
||||
typeof(Vector2Int),
|
||||
typeof(Vector3Int),
|
||||
typeof(Vector4),
|
||||
typeof(Quaternion),
|
||||
typeof(Color),
|
||||
typeof(Color32),
|
||||
typeof(Ray),
|
||||
typeof(Ray2D)
|
||||
};
|
||||
NetworkVariableSerialization<T>.Serializer = new UnmanagedTypeSerializer<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an unmanaged type that implements INetworkSerializable and will be serialized through a call to
|
||||
/// NetworkSerialize
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeSerializer_UnmanagedINetworkSerializable<T>() where T : unmanaged, INetworkSerializable
|
||||
{
|
||||
NetworkVariableSerialization<T>.Serializer = new UnmanagedNetworkSerializableSerializer<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a managed type that implements INetworkSerializable and will be serialized through a call to
|
||||
/// NetworkSerialize
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeSerializer_ManagedINetworkSerializable<T>() where T : class, INetworkSerializable, new()
|
||||
{
|
||||
NetworkVariableSerialization<T>.Serializer = new ManagedNetworkSerializableSerializer<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a FixedString type that will be serialized through FastBufferReader/FastBufferWriter's FixedString
|
||||
/// serializers
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeSerializer_FixedString<T>() where T : unmanaged, INativeList<byte>, IUTF8Bytes
|
||||
{
|
||||
NetworkVariableSerialization<T>.Serializer = new FixedStringSerializer<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a managed type that will be checked for equality using T.Equals()
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeEqualityChecker_ManagedIEquatable<T>() where T : class, IEquatable<T>
|
||||
{
|
||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.EqualityEqualsObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an unmanaged type that will be checked for equality using T.Equals()
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeEqualityChecker_UnmanagedIEquatable<T>() where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.EqualityEquals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an unmanaged type that will be checked for equality using memcmp and only considered
|
||||
/// equal if they are bitwise equivalent in memory
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeEqualityChecker_UnmanagedValueEquals<T>() where T : unmanaged
|
||||
{
|
||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.ValueEquals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a managed type that will be checked for equality using the == operator
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public static void InitializeEqualityChecker_ManagedClassEquals<T>() where T : class
|
||||
{
|
||||
NetworkVariableSerialization<T>.AreEqual = NetworkVariableSerialization<T>.ClassEquals;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -212,56 +273,59 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type the associated NetworkVariable is templated on</typeparam>
|
||||
[Serializable]
|
||||
public static class NetworkVariableSerialization<T> where T : unmanaged
|
||||
public static class NetworkVariableSerialization<T>
|
||||
{
|
||||
private static INetworkVariableSerializer<T> s_Serializer = GetSerializer();
|
||||
internal static INetworkVariableSerializer<T> Serializer = new FallbackSerializer<T>();
|
||||
|
||||
private static INetworkVariableSerializer<T> GetSerializer()
|
||||
internal delegate bool EqualsDelegate(ref T a, ref T b);
|
||||
internal static EqualsDelegate AreEqual;
|
||||
|
||||
// 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
|
||||
{
|
||||
if (NetworkVariableSerializationTypes.BaseSupportedTypes.Contains(typeof(T)))
|
||||
// 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;
|
||||
}
|
||||
|
||||
internal static bool EqualityEqualsObject<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class, IEquatable<TValueType>
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
return new UnmanagedTypeSerializer<T>();
|
||||
}
|
||||
if (typeof(INetworkSerializeByMemcpy).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
return new UnmanagedTypeSerializer<T>();
|
||||
}
|
||||
if (typeof(Enum).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
return new UnmanagedTypeSerializer<T>();
|
||||
return b == null;
|
||||
}
|
||||
|
||||
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(T)))
|
||||
if (b == null)
|
||||
{
|
||||
// Obtains "Open Instance Delegates" for the type's NetworkSerialize() methods -
|
||||
// one for an instance of the generic method taking BufferSerializerWriter as T,
|
||||
// one for an instance of the generic method taking BufferSerializerReader as T
|
||||
var writeMethod = (NetworkSerializableSerializer<T>.WriteValueDelegate)Delegate.CreateDelegate(typeof(NetworkSerializableSerializer<T>.WriteValueDelegate), null, typeof(T).GetMethod(nameof(INetworkSerializable.NetworkSerialize)).MakeGenericMethod(typeof(BufferSerializerWriter)));
|
||||
var readMethod = (NetworkSerializableSerializer<T>.ReadValueDelegate)Delegate.CreateDelegate(typeof(NetworkSerializableSerializer<T>.ReadValueDelegate), null, typeof(T).GetMethod(nameof(INetworkSerializable.NetworkSerialize)).MakeGenericMethod(typeof(BufferSerializerReader)));
|
||||
return new NetworkSerializableSerializer<T> { WriteValue = writeMethod, ReadValue = readMethod };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(IUTF8Bytes).IsAssignableFrom(typeof(T)) && typeof(INativeList<byte>).IsAssignableFrom(typeof(T)))
|
||||
{
|
||||
// Get "OpenInstanceDelegates" for the Length property (get and set, which are prefixed
|
||||
// with "get_" and "set_" under the hood and emitted as methods) and GetUnsafePtr()
|
||||
var getLength = (FixedStringSerializer<T>.GetLengthDelegate)Delegate.CreateDelegate(typeof(FixedStringSerializer<T>.GetLengthDelegate), null, typeof(T).GetMethod("get_" + nameof(INativeList<byte>.Length)));
|
||||
var setLength = (FixedStringSerializer<T>.SetLengthDelegate)Delegate.CreateDelegate(typeof(FixedStringSerializer<T>.SetLengthDelegate), null, typeof(T).GetMethod("set_" + nameof(INativeList<byte>.Length)));
|
||||
var getUnsafePtr = (FixedStringSerializer<T>.GetUnsafePtrDelegate)Delegate.CreateDelegate(typeof(FixedStringSerializer<T>.GetUnsafePtrDelegate), null, typeof(T).GetMethod(nameof(IUTF8Bytes.GetUnsafePtr)));
|
||||
return new FixedStringSerializer<T> { GetLength = getLength, SetLength = setLength, GetUnsafePtr = getUnsafePtr };
|
||||
}
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
return new FallbackSerializer<T>();
|
||||
internal static bool EqualityEquals<TValueType>(ref TValueType a, ref TValueType b) where TValueType : unmanaged, IEquatable<TValueType>
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
internal static bool ClassEquals<TValueType>(ref TValueType a, ref TValueType b) where TValueType : class
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
internal static void Write(FastBufferWriter writer, ref T value)
|
||||
{
|
||||
s_Serializer.Write(writer, ref value);
|
||||
Serializer.Write(writer, ref value);
|
||||
}
|
||||
|
||||
internal static void Read(FastBufferReader reader, out T value)
|
||||
internal static void Read(FastBufferReader reader, ref T value)
|
||||
{
|
||||
s_Serializer.Read(reader, out value);
|
||||
Serializer.Read(reader, ref value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user