using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEditor;
using UnityEngine;
namespace Unity.Netcode
{
///
/// Interface used by NetworkVariables to serialize them
///
///
internal interface INetworkVariableSerializer
{
// Write has to be taken by ref here because of INetworkSerializable
// Open Instance Delegates (pointers to methods without an instance attached to them)
// require the first parameter passed to them (the instance) to be passed by ref.
// So foo.Bar() becomes BarDelegate(ref foo);
// 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, ref T value);
public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue);
public void ReadDelta(FastBufferReader reader, ref T value);
internal void ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator);
public void Duplicate(in T value, ref T duplicatedValue);
}
///
/// Packing serializer for shorts
///
internal class ShortSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref short value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref short value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref short value, ref short previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref short value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out short value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in short value, ref short duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Packing serializer for shorts
///
internal class UshortSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref ushort value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref ushort value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref ushort value, ref ushort previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref ushort value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out ushort value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in ushort value, ref ushort duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Packing serializer for ints
///
internal class IntSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref int value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref int value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref int value, ref int previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref int value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out int value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in int value, ref int duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Packing serializer for ints
///
internal class UintSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref uint value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref uint value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref uint value, ref uint previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref uint value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out uint value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in uint value, ref uint duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Packing serializer for longs
///
internal class LongSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref long value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref long value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref long value, ref long previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref long value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out long value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in long value, ref long duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Packing serializer for longs
///
internal class UlongSerializer : INetworkVariableSerializer
{
public void Write(FastBufferWriter writer, ref ulong value)
{
BytePacker.WriteValueBitPacked(writer, value);
}
public void Read(FastBufferReader reader, ref ulong value)
{
ByteUnpacker.ReadValueBitPacked(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref ulong value, ref ulong previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref ulong value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out ulong value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in ulong value, ref ulong duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Basic serializer for unmanaged types.
/// This covers primitives, built-in unity types, and IForceSerializeByMemcpy
/// Since all of those ultimately end up calling WriteUnmanagedSafe, this simplifies things
/// by calling that directly - thus preventing us from having to have a specific T that meets
/// the specific constraints that the various generic WriteValue calls require.
///
///
internal class UnmanagedTypeSerializer : INetworkVariableSerializer where T : unmanaged
{
public void Write(FastBufferWriter writer, ref T value)
{
writer.WriteUnmanagedSafe(value);
}
public void Read(FastBufferReader reader, ref T value)
{
reader.ReadUnmanagedSafe(out value);
}
public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref T value)
{
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in T value, ref T duplicatedValue)
{
duplicatedValue = value;
}
}
internal class ListSerializer : INetworkVariableSerializer>
{
public void Write(FastBufferWriter writer, ref List value)
{
bool isNull = value == null;
writer.WriteValueSafe(isNull);
if (!isNull)
{
BytePacker.WriteValuePacked(writer, value.Count);
foreach (var item in value)
{
var reffable = item;
NetworkVariableSerialization.Write(writer, ref reffable);
}
}
}
public void Read(FastBufferReader reader, ref List value)
{
reader.ReadValueSafe(out bool isNull);
if (isNull)
{
value = null;
}
else
{
if (value == null)
{
value = new List();
}
ByteUnpacker.ReadValuePacked(reader, out int len);
if (len < value.Count)
{
value.RemoveRange(len, value.Count - len);
}
for (var i = 0; i < len; ++i)
{
// Read in place where possible
if (i < value.Count)
{
T item = value[i];
NetworkVariableSerialization.Read(reader, ref item);
value[i] = item;
}
else
{
T item = default;
NetworkVariableSerialization.Read(reader, ref item);
value.Add(item);
}
}
}
}
public void WriteDelta(FastBufferWriter writer, ref List value, ref List previousValue)
{
CollectionSerializationUtility.WriteListDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref List value)
{
CollectionSerializationUtility.ReadListDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out List value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in List value, ref List duplicatedValue)
{
if (duplicatedValue == null)
{
duplicatedValue = new List();
}
duplicatedValue.Clear();
foreach (var item in value)
{
// This handles the nested list scenario List>
T subValue = default;
NetworkVariableSerialization.Duplicate(item, ref subValue);
duplicatedValue.Add(subValue);
}
}
}
internal class HashSetSerializer : INetworkVariableSerializer> where T : IEquatable
{
public void Write(FastBufferWriter writer, ref HashSet value)
{
bool isNull = value == null;
writer.WriteValueSafe(isNull);
if (!isNull)
{
writer.WriteValueSafe(value.Count);
foreach (var item in value)
{
var reffable = item;
NetworkVariableSerialization.Write(writer, ref reffable);
}
}
}
public void Read(FastBufferReader reader, ref HashSet value)
{
reader.ReadValueSafe(out bool isNull);
if (isNull)
{
value = null;
}
else
{
if (value == null)
{
value = new HashSet();
}
else
{
value.Clear();
}
reader.ReadValueSafe(out int len);
for (var i = 0; i < len; ++i)
{
T item = default;
NetworkVariableSerialization.Read(reader, ref item);
value.Add(item);
}
}
}
public void WriteDelta(FastBufferWriter writer, ref HashSet value, ref HashSet previousValue)
{
CollectionSerializationUtility.WriteHashSetDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref HashSet value)
{
CollectionSerializationUtility.ReadHashSetDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out HashSet value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in HashSet value, ref HashSet duplicatedValue)
{
if (duplicatedValue == null)
{
duplicatedValue = new HashSet();
}
duplicatedValue.Clear();
foreach (var item in value)
{
// Handles nested HashSets
T subValue = default;
NetworkVariableSerialization.Duplicate(item, ref subValue);
duplicatedValue.Add(item);
}
}
}
internal class DictionarySerializer : INetworkVariableSerializer>
where TKey : IEquatable
{
public void Write(FastBufferWriter writer, ref Dictionary value)
{
bool isNull = value == null;
writer.WriteValueSafe(isNull);
if (!isNull)
{
writer.WriteValueSafe(value.Count);
foreach (var item in value)
{
(var key, var val) = (item.Key, item.Value);
NetworkVariableSerialization.Write(writer, ref key);
NetworkVariableSerialization.Write(writer, ref val);
}
}
}
public void Read(FastBufferReader reader, ref Dictionary value)
{
reader.ReadValueSafe(out bool isNull);
if (isNull)
{
value = null;
}
else
{
if (value == null)
{
value = new Dictionary();
}
else
{
value.Clear();
}
reader.ReadValueSafe(out int len);
for (var i = 0; i < len; ++i)
{
(TKey key, TVal val) = (default, default);
NetworkVariableSerialization.Read(reader, ref key);
NetworkVariableSerialization.Read(reader, ref val);
value.Add(key, val);
}
}
}
public void WriteDelta(FastBufferWriter writer, ref Dictionary value, ref Dictionary previousValue)
{
CollectionSerializationUtility.WriteDictionaryDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref Dictionary value)
{
CollectionSerializationUtility.ReadDictionaryDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out Dictionary value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in Dictionary value, ref Dictionary duplicatedValue)
{
if (duplicatedValue == null)
{
duplicatedValue = new Dictionary();
}
duplicatedValue.Clear();
foreach (var item in value)
{
// Handles nested dictionaries
TKey subKey = default;
TVal subValue = default;
NetworkVariableSerialization.Duplicate(item.Key, ref subKey);
NetworkVariableSerialization.Duplicate(item.Value, ref subValue);
duplicatedValue.Add(subKey, subValue);
}
}
}
internal class UnmanagedArraySerializer : INetworkVariableSerializer> where T : unmanaged
{
public void Write(FastBufferWriter writer, ref NativeArray value)
{
writer.WriteUnmanagedSafe(value);
}
public void Read(FastBufferReader reader, ref NativeArray value)
{
value.Dispose();
reader.ReadUnmanagedSafe(out value, Allocator.Persistent);
}
public void WriteDelta(FastBufferWriter writer, ref NativeArray value, ref NativeArray previousValue)
{
CollectionSerializationUtility.WriteNativeArrayDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeArray value)
{
CollectionSerializationUtility.ReadNativeArrayDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeArray value, Allocator allocator)
{
reader.ReadUnmanagedSafe(out value, allocator);
}
public void Duplicate(in NativeArray value, ref NativeArray duplicatedValue)
{
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
{
if (duplicatedValue.IsCreated)
{
duplicatedValue.Dispose();
}
duplicatedValue = new NativeArray(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
duplicatedValue.CopyFrom(value);
}
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
internal class UnmanagedListSerializer : INetworkVariableSerializer> where T : unmanaged
{
public void Write(FastBufferWriter writer, ref NativeList value)
{
writer.WriteUnmanagedSafe(value);
}
public void Read(FastBufferReader reader, ref NativeList value)
{
reader.ReadUnmanagedSafeInPlace(ref value);
}
public void WriteDelta(FastBufferWriter writer, ref NativeList value, ref NativeList previousValue)
{
CollectionSerializationUtility.WriteNativeListDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeList value)
{
CollectionSerializationUtility.ReadNativeListDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeList value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in NativeList value, ref NativeList duplicatedValue)
{
if (!duplicatedValue.IsCreated)
{
duplicatedValue = new NativeList(value.Length, Allocator.Persistent);
}
else if (value.Length != duplicatedValue.Length)
{
duplicatedValue.ResizeUninitialized(value.Length);
}
duplicatedValue.CopyFrom(value);
}
}
internal class NativeHashSetSerializer : INetworkVariableSerializer> where T : unmanaged, IEquatable
{
public void Write(FastBufferWriter writer, ref NativeHashSet value)
{
writer.WriteValueSafe(value);
}
public void Read(FastBufferReader reader, ref NativeHashSet value)
{
reader.ReadValueSafeInPlace(ref value);
}
public void WriteDelta(FastBufferWriter writer, ref NativeHashSet value, ref NativeHashSet previousValue)
{
CollectionSerializationUtility.WriteNativeHashSetDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeHashSet value)
{
CollectionSerializationUtility.ReadNativeHashSetDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeHashSet value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in NativeHashSet value, ref NativeHashSet duplicatedValue)
{
if (!duplicatedValue.IsCreated)
{
duplicatedValue = new NativeHashSet(value.Capacity, Allocator.Persistent);
}
duplicatedValue.Clear();
foreach (var item in value)
{
duplicatedValue.Add(item);
}
}
}
internal class NativeHashMapSerializer : INetworkVariableSerializer>
where TKey : unmanaged, IEquatable
where TVal : unmanaged
{
public void Write(FastBufferWriter writer, ref NativeHashMap value)
{
writer.WriteValueSafe(value);
}
public void Read(FastBufferReader reader, ref NativeHashMap value)
{
reader.ReadValueSafeInPlace(ref value);
}
public void WriteDelta(FastBufferWriter writer, ref NativeHashMap value, ref NativeHashMap previousValue)
{
CollectionSerializationUtility.WriteNativeHashMapDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeHashMap value)
{
CollectionSerializationUtility.ReadNativeHashMapDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeHashMap value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in NativeHashMap value, ref NativeHashMap duplicatedValue)
{
if (!duplicatedValue.IsCreated)
{
duplicatedValue = new NativeHashMap(value.Capacity, Allocator.Persistent);
}
duplicatedValue.Clear();
foreach (var item in value)
{
duplicatedValue.Add(item.Key, item.Value);
}
}
}
#endif
///
/// Serializer for FixedStrings
///
///
internal class FixedStringSerializer : INetworkVariableSerializer where T : unmanaged, INativeList, IUTF8Bytes
{
public void Write(FastBufferWriter writer, ref T value)
{
writer.WriteValueSafe(value);
}
public void Read(FastBufferReader reader, ref T value)
{
reader.ReadValueSafeInPlace(ref value);
}
// Because of how strings are generally used, it is likely that most strings will still write as full strings
// instead of deltas. This actually adds one byte to the data to encode that it was serialized in full.
// But the potential savings from a small change to a large string are valuable enough to be worth that extra
// byte.
public unsafe void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{
using var changes = new ResizableBitVector(Allocator.Temp);
int minLength = math.min(value.Length, previousValue.Length);
var numChanges = 0;
for (var i = 0; i < minLength; ++i)
{
var val = value[i];
var prevVal = previousValue[i];
if (val != prevVal)
{
++numChanges;
changes.Set(i);
}
}
for (var i = previousValue.Length; i < value.Length; ++i)
{
++numChanges;
changes.Set(i);
}
if (changes.GetSerializedSize() + FastBufferWriter.GetWriteSize() * numChanges > FastBufferWriter.GetWriteSize() * value.Length)
{
writer.WriteByteSafe(1);
writer.WriteValueSafe(value);
return;
}
writer.WriteByteSafe(0);
BytePacker.WriteValuePacked(writer, value.Length);
writer.WriteValueSafe(changes);
unsafe
{
byte* ptr = value.GetUnsafePtr();
for (int i = 0; i < value.Length; ++i)
{
if (changes.IsSet(i))
{
writer.WriteByteSafe(ptr[i]);
}
}
}
}
public unsafe void ReadDelta(FastBufferReader reader, ref T value)
{
// Writing can use the NativeArray logic as it is, but reading is a little different.
// Using the NativeArray logic for reading would result in length changes allocating a new NativeArray,
// which is not what we want for FixedString. With FixedString, the actual size of the data does not change,
// only an in-memory "length" value - so if the length changes, the only thing we want to do is change
// that value, and otherwise read everything in-place.
reader.ReadByteSafe(out byte full);
if (full == 1)
{
reader.ReadValueSafeInPlace(ref value);
return;
}
ByteUnpacker.ReadValuePacked(reader, out int length);
var changes = new ResizableBitVector(Allocator.Temp);
using var toDispose = changes;
{
reader.ReadNetworkSerializableInPlace(ref changes);
value.Length = length;
byte* ptr = value.GetUnsafePtr();
for (var i = 0; i < value.Length; ++i)
{
if (changes.IsSet(i))
{
reader.ReadByteSafe(out ptr[i]);
}
}
}
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in T value, ref T duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Serializer for FixedStrings
///
///
internal class FixedStringArraySerializer : INetworkVariableSerializer> where T : unmanaged, INativeList, IUTF8Bytes
{
public void Write(FastBufferWriter writer, ref NativeArray value)
{
writer.WriteValueSafe(value);
}
public void Read(FastBufferReader reader, ref NativeArray value)
{
value.Dispose();
reader.ReadValueSafe(out value, Allocator.Persistent);
}
public void WriteDelta(FastBufferWriter writer, ref NativeArray value, ref NativeArray previousValue)
{
CollectionSerializationUtility.WriteNativeArrayDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeArray value)
{
CollectionSerializationUtility.ReadNativeArrayDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeArray value, Allocator allocator)
{
reader.ReadValueSafe(out value, allocator);
}
public void Duplicate(in NativeArray value, ref NativeArray duplicatedValue)
{
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
{
if (duplicatedValue.IsCreated)
{
duplicatedValue.Dispose();
}
duplicatedValue = new NativeArray(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
duplicatedValue.CopyFrom(value);
}
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
///
/// Serializer for FixedStrings
///
///
internal class FixedStringListSerializer : INetworkVariableSerializer> where T : unmanaged, INativeList, IUTF8Bytes
{
public void Write(FastBufferWriter writer, ref NativeList value)
{
writer.WriteValueSafe(value);
}
public void Read(FastBufferReader reader, ref NativeList value)
{
reader.ReadValueSafeInPlace(ref value);
}
public void WriteDelta(FastBufferWriter writer, ref NativeList value, ref NativeList previousValue)
{
CollectionSerializationUtility.WriteNativeListDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeList value)
{
CollectionSerializationUtility.ReadNativeListDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeList value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in NativeList value, ref NativeList duplicatedValue)
{
if (!duplicatedValue.IsCreated)
{
duplicatedValue = new NativeList(value.Length, Allocator.Persistent);
}
else if (value.Length != duplicatedValue.Length)
{
duplicatedValue.ResizeUninitialized(value.Length);
}
duplicatedValue.CopyFrom(value);
}
}
#endif
///
/// Serializer for unmanaged INetworkSerializable types
///
///
internal class UnmanagedNetworkSerializableSerializer : INetworkVariableSerializer where T : unmanaged, INetworkSerializable
{
public void Write(FastBufferWriter writer, ref T value)
{
var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(writer));
value.NetworkSerialize(bufferSerializer);
}
public void Read(FastBufferReader reader, ref T value)
{
var bufferSerializer = new BufferSerializer(new BufferSerializerReader(reader));
value.NetworkSerialize(bufferSerializer);
}
public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{
if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null)
{
UserNetworkVariableSerialization.WriteDelta(writer, value, previousValue);
return;
}
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref T value)
{
if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null)
{
UserNetworkVariableSerialization.ReadDelta(reader, ref value);
return;
}
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in T value, ref T duplicatedValue)
{
duplicatedValue = value;
}
}
///
/// Serializer for unmanaged INetworkSerializable types
///
///
internal class UnmanagedNetworkSerializableArraySerializer : INetworkVariableSerializer> where T : unmanaged, INetworkSerializable
{
public void Write(FastBufferWriter writer, ref NativeArray value)
{
writer.WriteNetworkSerializable(value);
}
public void Read(FastBufferReader reader, ref NativeArray value)
{
value.Dispose();
reader.ReadNetworkSerializable(out value, Allocator.Persistent);
}
public void WriteDelta(FastBufferWriter writer, ref NativeArray value, ref NativeArray previousValue)
{
CollectionSerializationUtility.WriteNativeArrayDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeArray value)
{
CollectionSerializationUtility.ReadNativeArrayDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeArray value, Allocator allocator)
{
reader.ReadNetworkSerializable(out value, allocator);
}
public void Duplicate(in NativeArray value, ref NativeArray duplicatedValue)
{
if (!duplicatedValue.IsCreated || duplicatedValue.Length != value.Length)
{
if (duplicatedValue.IsCreated)
{
duplicatedValue.Dispose();
}
duplicatedValue = new NativeArray(value.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
duplicatedValue.CopyFrom(value);
}
}
#if UNITY_NETCODE_NATIVE_COLLECTION_SUPPORT
///
/// Serializer for unmanaged INetworkSerializable types
///
///
internal class UnmanagedNetworkSerializableListSerializer : INetworkVariableSerializer> where T : unmanaged, INetworkSerializable
{
public void Write(FastBufferWriter writer, ref NativeList value)
{
writer.WriteNetworkSerializable(value);
}
public void Read(FastBufferReader reader, ref NativeList value)
{
reader.ReadNetworkSerializableInPlace(ref value);
}
public void WriteDelta(FastBufferWriter writer, ref NativeList value, ref NativeList previousValue)
{
CollectionSerializationUtility.WriteNativeListDelta(writer, ref value, ref previousValue);
}
public void ReadDelta(FastBufferReader reader, ref NativeList value)
{
CollectionSerializationUtility.ReadNativeListDelta(reader, ref value);
}
void INetworkVariableSerializer>.ReadWithAllocator(FastBufferReader reader, out NativeList value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in NativeList value, ref NativeList duplicatedValue)
{
if (!duplicatedValue.IsCreated)
{
duplicatedValue = new NativeList(value.Length, Allocator.Persistent);
}
else if (value.Length != duplicatedValue.Length)
{
duplicatedValue.ResizeUninitialized(value.Length);
}
duplicatedValue.CopyFrom(value);
}
}
#endif
///
/// Serializer for managed INetworkSerializable types, which differs from the unmanaged implementation in that it
/// has to be null-aware
///
internal class ManagedNetworkSerializableSerializer : INetworkVariableSerializer where T : class, INetworkSerializable, new()
{
public void Write(FastBufferWriter writer, ref T value)
{
var bufferSerializer = new BufferSerializer(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(new BufferSerializerReader(reader));
bool isNull = false;
bufferSerializer.SerializeValue(ref isNull);
if (isNull)
{
value = null;
}
else
{
if (value == null)
{
value = new T();
}
value.NetworkSerialize(bufferSerializer);
}
}
public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{
if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null)
{
UserNetworkVariableSerialization.WriteDelta(writer, value, previousValue);
return;
}
Write(writer, ref value);
}
public void ReadDelta(FastBufferReader reader, ref T value)
{
if (UserNetworkVariableSerialization.WriteDelta != null && UserNetworkVariableSerialization.ReadDelta != null)
{
UserNetworkVariableSerialization.ReadDelta(reader, ref value);
return;
}
Read(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in T value, ref T duplicatedValue)
{
using var writer = new FastBufferWriter(256, Allocator.Temp, int.MaxValue);
var refValue = value;
Write(writer, ref refValue);
using var reader = new FastBufferReader(writer, Allocator.None);
Read(reader, ref duplicatedValue);
}
}
///
/// This class is used to register user serialization with NetworkVariables for types
/// that are serialized via user serialization, such as with FastBufferReader and FastBufferWriter
/// extension methods. Finding those methods isn't achievable efficiently at runtime, so this allows
/// users to tell NetworkVariable about those extension methods (or simply pass in a lambda)
///
///
public class UserNetworkVariableSerialization
{
///
/// The write value delegate handler definition
///
/// The to write the value of type `T`
/// The value of type `T` to be written
public delegate void WriteValueDelegate(FastBufferWriter writer, in T value);
///
/// The write value delegate handler definition
///
/// The to write the value of type `T`
/// The value of type `T` to be written
public delegate void WriteDeltaDelegate(FastBufferWriter writer, in T value, in T previousValue);
///
/// The read value delegate handler definition
///
/// The to read the value of type `T`
/// The value of type `T` to be read
public delegate void ReadValueDelegate(FastBufferReader reader, out T value);
///
/// The read value delegate handler definition
///
/// The to read the value of type `T`
/// The value of type `T` to be read
public delegate void ReadDeltaDelegate(FastBufferReader reader, ref T value);
///
/// The read value delegate handler definition
///
/// The to read the value of type `T`
/// The value of type `T` to be read
public delegate void DuplicateValueDelegate(in T value, ref T duplicatedValue);
///
/// Callback to write a value
///
public static WriteValueDelegate WriteValue;
///
/// Callback to read a value
///
public static ReadValueDelegate ReadValue;
///
/// Callback to write a delta between two values, based on computing the difference between the previous and
/// current values.
///
public static WriteDeltaDelegate WriteDelta;
///
/// Callback to read a delta, applying only select changes to the current value.
///
public static ReadDeltaDelegate ReadDelta;
///
/// Callback to create a duplicate of a value, used to check for dirty status.
///
public static DuplicateValueDelegate DuplicateValue;
}
///
/// This class is instantiated for types that we can't determine ahead of time are serializable - types
/// that don't meet any of the constraints for methods that are available on FastBufferReader and
/// FastBufferWriter. These types may or may not be serializable through extension methods. To ensure
/// the user has time to pass in the delegates to UserNetworkVariableSerialization, the existence
/// of user serialization isn't checked until it's used, so if no serialization is provided, this
/// will throw an exception when an object containing the relevant NetworkVariable is spawned.
///
///
internal class FallbackSerializer : INetworkVariableSerializer
{
private void ThrowArgumentError()
{
throw new ArgumentException($"Serialization has not been generated for type {typeof(T).FullName}. This can be addressed by adding a [{nameof(GenerateSerializationForGenericParameterAttribute)}] to your generic class that serializes this value (if you are using one), adding [{nameof(GenerateSerializationForTypeAttribute)}(typeof({typeof(T).FullName})] to the class or method that is attempting to serialize it, or creating a field on a {nameof(NetworkBehaviour)} of type {nameof(NetworkVariable)}. If this error continues to appear after doing one of those things and this is a type you can change, then either implement {nameof(INetworkSerializable)} or mark it as serializable by memcpy by adding {nameof(INetworkSerializeByMemcpy)} to its interface list to enable automatic serialization generation. If not, assign serialization code to {nameof(UserNetworkVariableSerialization)}.{nameof(UserNetworkVariableSerialization.WriteValue)}, {nameof(UserNetworkVariableSerialization)}.{nameof(UserNetworkVariableSerialization.ReadValue)}, and {nameof(UserNetworkVariableSerialization)}.{nameof(UserNetworkVariableSerialization.DuplicateValue)}, or if it's serializable by memcpy (contains no pointers), wrap it in {typeof(ForceNetworkSerializeByMemcpy<>).Name}.");
}
public void Write(FastBufferWriter writer, ref T value)
{
if (UserNetworkVariableSerialization.ReadValue == null || UserNetworkVariableSerialization.WriteValue == null || UserNetworkVariableSerialization.DuplicateValue == null)
{
ThrowArgumentError();
}
UserNetworkVariableSerialization.WriteValue(writer, value);
}
public void Read(FastBufferReader reader, ref T value)
{
if (UserNetworkVariableSerialization.ReadValue == null || UserNetworkVariableSerialization.WriteValue == null || UserNetworkVariableSerialization.DuplicateValue == null)
{
ThrowArgumentError();
}
UserNetworkVariableSerialization.ReadValue(reader, out value);
}
public void WriteDelta(FastBufferWriter writer, ref T value, ref T previousValue)
{
if (UserNetworkVariableSerialization.ReadValue == null || UserNetworkVariableSerialization.WriteValue == null || UserNetworkVariableSerialization.DuplicateValue == null)
{
ThrowArgumentError();
}
if (UserNetworkVariableSerialization.WriteDelta == null || UserNetworkVariableSerialization.ReadDelta == null)
{
UserNetworkVariableSerialization.WriteValue(writer, value);
return;
}
UserNetworkVariableSerialization.WriteDelta(writer, value, previousValue);
}
public void ReadDelta(FastBufferReader reader, ref T value)
{
if (UserNetworkVariableSerialization.ReadValue == null || UserNetworkVariableSerialization.WriteValue == null || UserNetworkVariableSerialization.DuplicateValue == null)
{
ThrowArgumentError();
}
if (UserNetworkVariableSerialization.WriteDelta == null || UserNetworkVariableSerialization.ReadDelta == null)
{
UserNetworkVariableSerialization.ReadValue(reader, out value);
return;
}
UserNetworkVariableSerialization.ReadDelta(reader, ref value);
}
void INetworkVariableSerializer.ReadWithAllocator(FastBufferReader reader, out T value, Allocator allocator)
{
throw new NotImplementedException();
}
public void Duplicate(in T value, ref T duplicatedValue)
{
if (UserNetworkVariableSerialization.ReadValue == null || UserNetworkVariableSerialization.WriteValue == null || UserNetworkVariableSerialization.DuplicateValue == null)
{
ThrowArgumentError();
}
UserNetworkVariableSerialization.DuplicateValue(value, ref duplicatedValue);
}
}
///
/// 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 .
/// 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.)
///
public static class NetworkVariableSerializationTypes
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
#if UNITY_EDITOR
[InitializeOnLoadMethod]
#endif
internal static void InitializeIntegerSerialization()
{
NetworkVariableSerialization.Serializer = new ShortSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization.ValueEquals;
NetworkVariableSerialization.Serializer = new UshortSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization.ValueEquals;
NetworkVariableSerialization.Serializer = new IntSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization.ValueEquals;
NetworkVariableSerialization.Serializer = new UintSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization.ValueEquals;
NetworkVariableSerialization.Serializer = new LongSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization.ValueEquals;
NetworkVariableSerialization.Serializer = new UlongSerializer();
NetworkVariableSerialization.AreEqual = NetworkVariableSerialization