com.unity.netcode.gameobjects@1.0.0-pre.8
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.0.0-pre.8] - 2022-04-27 ### Changed - `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901) ### Removed - Removed `SIPTransport` (#1870) - Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs). ### Fixed - Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890) - Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884) - Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883) - Fixed an issue where UNetTransport.StartServer would return success even if the underlying transport failed to start (#854) - Passing generic types to RPCs no longer causes a native crash (#1901) - Fixed an issue where calling `Shutdown` on a `NetworkManager` that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877)
This commit is contained in:
@@ -8,7 +8,7 @@ namespace Unity.Netcode
|
||||
/// Event based NetworkVariable container for syncing Lists
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type for the list</typeparam>
|
||||
public class NetworkList<T> : NetworkVariableBase where T : unmanaged, IEquatable<T>
|
||||
public class NetworkList<T> : NetworkVariableSerialization<T> where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
private NativeList<T> m_List = new NativeList<T>(64, Allocator.Persistent);
|
||||
private NativeList<NetworkListEvent<T>> m_DirtyEvents = new NativeList<NetworkListEvent<T>>(64, Allocator.Persistent);
|
||||
@@ -72,18 +72,18 @@ namespace Unity.Netcode
|
||||
{
|
||||
case NetworkListEvent<T>.EventType.Add:
|
||||
{
|
||||
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||
Write(writer, m_DirtyEvents[i].Value);
|
||||
}
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.Insert:
|
||||
{
|
||||
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
||||
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||
Write(writer, m_DirtyEvents[i].Value);
|
||||
}
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.Remove:
|
||||
{
|
||||
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||
Write(writer, m_DirtyEvents[i].Value);
|
||||
}
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.RemoveAt:
|
||||
@@ -94,7 +94,7 @@ namespace Unity.Netcode
|
||||
case NetworkListEvent<T>.EventType.Value:
|
||||
{
|
||||
writer.WriteValueSafe(m_DirtyEvents[i].Index);
|
||||
NetworkVariable<T>.Write(writer, m_DirtyEvents[i].Value);
|
||||
Write(writer, m_DirtyEvents[i].Value);
|
||||
}
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.Clear:
|
||||
@@ -112,7 +112,7 @@ namespace Unity.Netcode
|
||||
writer.WriteValueSafe((ushort)m_List.Length);
|
||||
for (int i = 0; i < m_List.Length; i++)
|
||||
{
|
||||
NetworkVariable<T>.Write(writer, m_List[i]);
|
||||
Write(writer, m_List[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Unity.Netcode
|
||||
reader.ReadValueSafe(out ushort count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
NetworkVariable<T>.Read(reader, out T value);
|
||||
Read(reader, out T value);
|
||||
m_List.Add(value);
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ namespace Unity.Netcode
|
||||
{
|
||||
case NetworkListEvent<T>.EventType.Add:
|
||||
{
|
||||
NetworkVariable<T>.Read(reader, out T value);
|
||||
Read(reader, out T value);
|
||||
m_List.Add(value);
|
||||
|
||||
if (OnListChanged != null)
|
||||
@@ -166,7 +166,7 @@ namespace Unity.Netcode
|
||||
case NetworkListEvent<T>.EventType.Insert:
|
||||
{
|
||||
reader.ReadValueSafe(out int index);
|
||||
NetworkVariable<T>.Read(reader, out T value);
|
||||
Read(reader, out T value);
|
||||
m_List.InsertRangeWithBeginEnd(index, index + 1);
|
||||
m_List[index] = value;
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace Unity.Netcode
|
||||
break;
|
||||
case NetworkListEvent<T>.EventType.Remove:
|
||||
{
|
||||
NetworkVariable<T>.Read(reader, out T value);
|
||||
Read(reader, out T value);
|
||||
int index = m_List.IndexOf(value);
|
||||
if (index == -1)
|
||||
{
|
||||
@@ -253,7 +253,7 @@ namespace Unity.Netcode
|
||||
case NetworkListEvent<T>.EventType.Value:
|
||||
{
|
||||
reader.ReadValueSafe(out int index);
|
||||
NetworkVariable<T>.Read(reader, out T value);
|
||||
Read(reader, out T value);
|
||||
if (index >= m_List.Length)
|
||||
{
|
||||
throw new Exception("Shouldn't be here, index is higher than list length");
|
||||
|
||||
@@ -9,55 +9,8 @@ namespace Unity.Netcode
|
||||
/// A variable that can be synchronized over the network.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
|
||||
public class NetworkVariable<T> : NetworkVariableSerialization<T> where T : unmanaged
|
||||
{
|
||||
// Functions that know how to serialize INetworkSerializable
|
||||
internal static void WriteNetworkSerializable<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : INetworkSerializable, new()
|
||||
{
|
||||
writer.WriteNetworkSerializable(value);
|
||||
}
|
||||
internal static void ReadNetworkSerializable<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : INetworkSerializable, new()
|
||||
{
|
||||
reader.ReadNetworkSerializable(out value);
|
||||
}
|
||||
|
||||
// Functions that serialize other types
|
||||
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged
|
||||
{
|
||||
writer.WriteValueSafe(value);
|
||||
}
|
||||
|
||||
private static void ReadValue<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged
|
||||
{
|
||||
reader.ReadValueSafe(out value);
|
||||
}
|
||||
|
||||
internal delegate void WriteDelegate<TForMethod>(FastBufferWriter writer, in TForMethod value);
|
||||
|
||||
internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out TForMethod value);
|
||||
|
||||
// These static delegates provide the right implementation for writing and reading a particular network variable type.
|
||||
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
||||
//
|
||||
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
||||
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
||||
//
|
||||
// In the future we may be able to use this to provide packing implementations for floats and integers to optimize bandwidth usage.
|
||||
//
|
||||
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
||||
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||
// *both* of which would cause a boxing allocation. Alternatively, NetworkVariable could have been split into
|
||||
// NetworkVariable and NetworkSerializableVariable or something like that, which would have caused a poor
|
||||
// user experience and an API that's easier to get wrong than right. This is a bit ugly on the implementation
|
||||
// side, but it gets the best achievable user experience and performance.
|
||||
internal static WriteDelegate<T> Write = WriteValue;
|
||||
internal static ReadDelegate<T> Read = ReadValue;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delegate type for value changed event
|
||||
/// </summary>
|
||||
@@ -143,6 +96,11 @@ namespace Unity.Netcode
|
||||
/// <param name="keepDirtyDelta">Whether or not the container should keep the dirty delta, or mark the delta as consumed</param>
|
||||
public override void ReadDelta(FastBufferReader reader, bool keepDirtyDelta)
|
||||
{
|
||||
// todo:
|
||||
// keepDirtyDelta marks a variable received as dirty and causes the server to send the value to clients
|
||||
// In a prefect world, whether a variable was A) modified locally or B) received and needs retransmit
|
||||
// would be stored in different fields
|
||||
|
||||
T previousValue = m_InternalValue;
|
||||
Read(reader, out m_InternalValue);
|
||||
|
||||
|
||||
@@ -94,6 +94,14 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ClientId of the owning client
|
||||
/// </summary>
|
||||
internal ulong OwnerClientId()
|
||||
{
|
||||
return m_NetworkBehaviour.NetworkObject.OwnerClientId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the dirty changes, that is, the changes since the variable was last dirty, to the writer
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
public class NetworkVariableHelper
|
||||
@@ -13,10 +16,62 @@ namespace Unity.Netcode
|
||||
// side, but it gets the best achievable user experience and performance.
|
||||
//
|
||||
// RuntimeAccessModifiersILPP will make this `public`
|
||||
internal static void InitializeDelegates<T>() where T : unmanaged, INetworkSerializable
|
||||
internal static void InitializeDelegatesNetworkSerializable<T>() where T : unmanaged, INetworkSerializable
|
||||
{
|
||||
NetworkVariable<T>.Write = NetworkVariable<T>.WriteNetworkSerializable;
|
||||
NetworkVariable<T>.Read = NetworkVariable<T>.ReadNetworkSerializable;
|
||||
NetworkVariableSerialization<T>.SetWriteDelegate(NetworkVariableSerialization<T>.WriteNetworkSerializable);
|
||||
NetworkVariableSerialization<T>.SetReadDelegate(NetworkVariableSerialization<T>.ReadNetworkSerializable);
|
||||
}
|
||||
internal static void InitializeDelegatesStruct<T>() where T : unmanaged, INetworkSerializeByMemcpy
|
||||
{
|
||||
NetworkVariableSerialization<T>.SetWriteDelegate(NetworkVariableSerialization<T>.WriteStruct);
|
||||
NetworkVariableSerialization<T>.SetReadDelegate(NetworkVariableSerialization<T>.ReadStruct);
|
||||
}
|
||||
internal static void InitializeDelegatesEnum<T>() where T : unmanaged, Enum
|
||||
{
|
||||
NetworkVariableSerialization<T>.SetWriteDelegate(NetworkVariableSerialization<T>.WriteEnum);
|
||||
NetworkVariableSerialization<T>.SetReadDelegate(NetworkVariableSerialization<T>.ReadEnum);
|
||||
}
|
||||
internal static void InitializeDelegatesPrimitive<T>() where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>
|
||||
{
|
||||
NetworkVariableSerialization<T>.SetWriteDelegate(NetworkVariableSerialization<T>.WritePrimitive);
|
||||
NetworkVariableSerialization<T>.SetReadDelegate(NetworkVariableSerialization<T>.ReadPrimitive);
|
||||
}
|
||||
|
||||
internal static void InitializeAllBaseDelegates()
|
||||
{
|
||||
// Built-in C# types, serialized through a generic method
|
||||
InitializeDelegatesPrimitive<bool>();
|
||||
InitializeDelegatesPrimitive<byte>();
|
||||
InitializeDelegatesPrimitive<sbyte>();
|
||||
InitializeDelegatesPrimitive<char>();
|
||||
InitializeDelegatesPrimitive<decimal>();
|
||||
InitializeDelegatesPrimitive<float>();
|
||||
InitializeDelegatesPrimitive<double>();
|
||||
InitializeDelegatesPrimitive<short>();
|
||||
InitializeDelegatesPrimitive<ushort>();
|
||||
InitializeDelegatesPrimitive<int>();
|
||||
InitializeDelegatesPrimitive<uint>();
|
||||
InitializeDelegatesPrimitive<long>();
|
||||
InitializeDelegatesPrimitive<ulong>();
|
||||
|
||||
// Built-in Unity types, serialized with specific overloads because they're structs without ISerializeByMemcpy attached
|
||||
NetworkVariableSerialization<Vector2>.SetWriteDelegate((FastBufferWriter writer, in Vector2 value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Vector3>.SetWriteDelegate((FastBufferWriter writer, in Vector3 value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Vector4>.SetWriteDelegate((FastBufferWriter writer, in Vector4 value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Quaternion>.SetWriteDelegate((FastBufferWriter writer, in Quaternion value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Color>.SetWriteDelegate((FastBufferWriter writer, in Color value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Color32>.SetWriteDelegate((FastBufferWriter writer, in Color32 value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Ray>.SetWriteDelegate((FastBufferWriter writer, in Ray value) => { writer.WriteValueSafe(value); });
|
||||
NetworkVariableSerialization<Ray2D>.SetWriteDelegate((FastBufferWriter writer, in Ray2D value) => { writer.WriteValueSafe(value); });
|
||||
|
||||
NetworkVariableSerialization<Vector2>.SetReadDelegate((FastBufferReader reader, out Vector2 value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Vector3>.SetReadDelegate((FastBufferReader reader, out Vector3 value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Vector4>.SetReadDelegate((FastBufferReader reader, out Vector4 value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Quaternion>.SetReadDelegate((FastBufferReader reader, out Quaternion value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Color>.SetReadDelegate((FastBufferReader reader, out Color value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Color32>.SetReadDelegate((FastBufferReader reader, out Color32 value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Ray>.SetReadDelegate((FastBufferReader reader, out Ray value) => { reader.ReadValueSafe(out value); });
|
||||
NetworkVariableSerialization<Ray2D>.SetReadDelegate((FastBufferReader reader, out Ray2D value) => { reader.ReadValueSafe(out value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
169
Runtime/NetworkVariable/NetworkVariableSerialization.cs
Normal file
169
Runtime/NetworkVariable/NetworkVariableSerialization.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
/// <summary>
|
||||
/// Support methods for reading/writing NetworkVariables
|
||||
/// 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 includes various read/write delegates
|
||||
/// based on which constraints are met by `T`. These constraints are set up by `NetworkVariableHelpers`,
|
||||
/// which is invoked by code generated by ILPP during module load.
|
||||
/// (As it turns out, IL has support for a module initializer that C# doesn't expose.)
|
||||
/// This installs the correct delegate for each `T` to ensure that each type is serialized properly.
|
||||
///
|
||||
/// Any type that inherits from `NetworkVariableSerialization<T>` will implicitly result in any `T`
|
||||
/// passed to it being picked up and initialized by ILPP.
|
||||
///
|
||||
/// The methods here, despite being static, are `protected` specifically to ensure that anything that
|
||||
/// wants access to them has to inherit from this base class, thus enabling ILPP to find and initialize it.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class NetworkVariableSerialization<T> : NetworkVariableBase where T : unmanaged
|
||||
{
|
||||
// Functions that know how to serialize INetworkSerializable
|
||||
internal static void WriteNetworkSerializable<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged, INetworkSerializable
|
||||
{
|
||||
writer.WriteNetworkSerializable(value);
|
||||
}
|
||||
|
||||
internal static void ReadNetworkSerializable<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged, INetworkSerializable
|
||||
{
|
||||
reader.ReadNetworkSerializable(out value);
|
||||
}
|
||||
|
||||
// Functions that serialize structs
|
||||
internal static void WriteStruct<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged, INetworkSerializeByMemcpy
|
||||
{
|
||||
writer.WriteValueSafe(value);
|
||||
}
|
||||
internal static void ReadStruct<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged, INetworkSerializeByMemcpy
|
||||
{
|
||||
reader.ReadValueSafe(out value);
|
||||
}
|
||||
|
||||
// Functions that serialize enums
|
||||
internal static void WriteEnum<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged, Enum
|
||||
{
|
||||
writer.WriteValueSafe(value);
|
||||
}
|
||||
internal static void ReadEnum<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged, Enum
|
||||
{
|
||||
reader.ReadValueSafe(out value);
|
||||
}
|
||||
|
||||
// Functions that serialize other types
|
||||
internal static void WritePrimitive<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged, IComparable, IConvertible, IComparable<TForMethod>, IEquatable<TForMethod>
|
||||
{
|
||||
writer.WriteValueSafe(value);
|
||||
}
|
||||
|
||||
internal static void ReadPrimitive<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged, IComparable, IConvertible, IComparable<TForMethod>, IEquatable<TForMethod>
|
||||
{
|
||||
reader.ReadValueSafe(out value);
|
||||
}
|
||||
|
||||
// Should never be reachable at runtime. All calls to this should be replaced with the correct
|
||||
// call above by ILPP.
|
||||
private static void WriteValue<TForMethod>(FastBufferWriter writer, in TForMethod value)
|
||||
where TForMethod : unmanaged
|
||||
{
|
||||
if (value is INetworkSerializable)
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesNetworkSerializable)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else if (value is INetworkSerializeByMemcpy)
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesStruct)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else if (value is Enum)
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesEnum)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Type {typeof(T).FullName} is not serializable - it must implement either INetworkSerializable or ISerializeByMemcpy");
|
||||
|
||||
}
|
||||
NetworkVariableSerialization<TForMethod>.Write(writer, value);
|
||||
}
|
||||
|
||||
private static void ReadValue<TForMethod>(FastBufferReader reader, out TForMethod value)
|
||||
where TForMethod : unmanaged
|
||||
{
|
||||
if (typeof(INetworkSerializable).IsAssignableFrom(typeof(TForMethod)))
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesNetworkSerializable)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else if (typeof(INetworkSerializeByMemcpy).IsAssignableFrom(typeof(TForMethod)))
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesStruct)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else if (typeof(Enum).IsAssignableFrom(typeof(TForMethod)))
|
||||
{
|
||||
typeof(NetworkVariableHelper).GetMethod(nameof(NetworkVariableHelper.InitializeDelegatesEnum)).MakeGenericMethod(typeof(TForMethod)).Invoke(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Type {typeof(T).FullName} is not serializable - it must implement either INetworkSerializable or ISerializeByMemcpy");
|
||||
|
||||
}
|
||||
NetworkVariableSerialization<TForMethod>.Read(reader, out value);
|
||||
}
|
||||
|
||||
protected internal delegate void WriteDelegate<TForMethod>(FastBufferWriter writer, in TForMethod value);
|
||||
|
||||
protected internal delegate void ReadDelegate<TForMethod>(FastBufferReader reader, out TForMethod value);
|
||||
|
||||
// These static delegates provide the right implementation for writing and reading a particular network variable type.
|
||||
// For most types, these default to WriteValue() and ReadValue(), which perform simple memcpy operations.
|
||||
//
|
||||
// INetworkSerializableILPP will generate startup code that will set it to WriteNetworkSerializable()
|
||||
// and ReadNetworkSerializable() for INetworkSerializable types, which will call NetworkSerialize().
|
||||
//
|
||||
// In the future we may be able to use this to provide packing implementations for floats and integers to optimize bandwidth usage.
|
||||
//
|
||||
// The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this,
|
||||
// NetworkVariable would need to do a `var is INetworkSerializable` check, and then cast to INetworkSerializable,
|
||||
// *both* of which would cause a boxing allocation. Alternatively, NetworkVariable could have been split into
|
||||
// NetworkVariable and NetworkSerializableVariable or something like that, which would have caused a poor
|
||||
// user experience and an API that's easier to get wrong than right. This is a bit ugly on the implementation
|
||||
// side, but it gets the best achievable user experience and performance.
|
||||
private static WriteDelegate<T> s_Write = WriteValue;
|
||||
private static ReadDelegate<T> s_Read = ReadValue;
|
||||
|
||||
protected static void Write(FastBufferWriter writer, in T value)
|
||||
{
|
||||
s_Write(writer, value);
|
||||
}
|
||||
|
||||
protected static void Read(FastBufferReader reader, out T value)
|
||||
{
|
||||
s_Read(reader, out value);
|
||||
}
|
||||
|
||||
internal static void SetWriteDelegate(WriteDelegate<T> write)
|
||||
{
|
||||
s_Write = write;
|
||||
}
|
||||
|
||||
internal static void SetReadDelegate(ReadDelegate<T> read)
|
||||
{
|
||||
s_Read = read;
|
||||
}
|
||||
|
||||
protected NetworkVariableSerialization(
|
||||
NetworkVariableReadPermission readPerm = DefaultReadPerm,
|
||||
NetworkVariableWritePermission writePerm = DefaultWritePerm)
|
||||
: base(readPerm, writePerm)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c6ef5fdf2e94ec3b4ce8086d52700b3
|
||||
timeCreated: 1650985453
|
||||
Reference in New Issue
Block a user