This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/Runtime/Serializing/Reader.cs

1422 lines
47 KiB
C#
Raw Permalink Normal View History

2023-05-31 17:32:21 +02:00
using FishNet.Connection;
using FishNet.Documenting;
using FishNet.Managing;
using FishNet.Object;
using FishNet.Object.Prediction;
using FishNet.Serializing.Helping;
using FishNet.Transporting;
using FishNet.Utility.Constant;
using FishNet.Utility.Extension;
using FishNet.Utility.Performance;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
//Required for internal tests.
[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)]
namespace FishNet.Serializing
{
/// <summary>
/// Used for read references to generic types.
/// </summary>
/// <typeparam name="T"></typeparam>
[APIExclude]
public static class GenericReader<T>
{
public static Func<Reader, T> Read { internal get; set; }
public static Func<Reader, AutoPackType, T> ReadAutoPack { internal get; set; }
}
/// <summary>
/// Reads data from a buffer.
/// </summary>
public class Reader
{
#region Public.
/// <summary>
/// Capacity of the buffer.
/// </summary>
public int Capacity => _buffer.Length;
/// <summary>
/// NetworkManager for this reader. Used to lookup objects.
/// </summary>
public NetworkManager NetworkManager;
/// <summary>
/// Offset within the buffer when the reader was created.
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Position for the next read.
/// </summary>
public int Position;
/// <summary>
/// Total number of bytes available within the buffer.
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Bytes remaining to be read. This value is Length - Position.
/// </summary>
public int Remaining => ((Length + Offset) - Position);
#endregion
#region Internal.
/// <summary>
/// NetworkConnection that this data came from.
/// Value may not always be set.
/// </summary>
public NetworkConnection NetworkConnection { get; private set; }
#if UNITY_EDITOR || DEVELOPMENT_BUILD
/// <summary>
/// Last NetworkObject parsed.
/// </summary>
public static NetworkObject LastNetworkObject { get; private set; }
/// <summary>
/// Last NetworkBehaviour parsed.
/// </summary>
public static NetworkBehaviour LastNetworkBehaviour { get; private set; }
#endif
#endregion
#region Private.
/// <summary>
/// Data being read.
/// </summary>
private byte[] _buffer;
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Reader(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null)
{
Initialize(bytes, networkManager, networkConnection);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Reader(ArraySegment<byte> segment, NetworkManager networkManager, NetworkConnection networkConnection = null)
{
Initialize(segment, networkManager, networkConnection);
}
/// <summary>
/// Outputs reader to string.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"Position: {Position}, Length: {Length}, Buffer: {BitConverter.ToString(_buffer, Offset, Length)}.";
}
/// <summary>
/// Initializes this reader with data.
/// </summary>
/// <param name="bytes"></param>
/// <param name="networkManager"></param>
internal void Initialize(ArraySegment<byte> bytes, NetworkManager networkManager, NetworkConnection networkConnection = null)
{
if (bytes.Array == null)
{
if (_buffer == null)
_buffer = new byte[0];
}
else
{
_buffer = bytes.Array;
}
Position = bytes.Offset;
Offset = bytes.Offset;
Length = bytes.Count;
NetworkManager = networkManager;
NetworkConnection = networkConnection;
}
/// <summary>
/// Initializes this reader with data.
/// </summary>
/// <param name="bytes"></param>
/// <param name="networkManager"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Initialize(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null)
{
Initialize(new ArraySegment<byte>(bytes), networkManager, networkConnection);
}
/// <summary>
/// Reads a dictionary.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Obsolete("Use ReadDictionaryAllocated.")]
public Dictionary<TKey, TValue> ReadDictionary<TKey, TValue>()
{
return ReadDictionaryAllocated<TKey, TValue>();
}
/// <summary>
/// Reads a dictionary.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dictionary<TKey, TValue> ReadDictionaryAllocated<TKey, TValue>()
{
bool isNull = ReadBoolean();
if (isNull)
return null;
int count = ReadInt32();
Dictionary<TKey, TValue> result = new Dictionary<TKey, TValue>(count);
for (int i = 0; i < count; i++)
{
TKey key = Read<TKey>();
TValue value = Read<TValue>();
result.Add(key, value);
}
return result;
}
/// <summary>
/// Reads length. This method is used to make debugging easier.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int ReadLength()
{
return ReadInt32();
}
/// <summary>
/// Reads a packetId.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal PacketId ReadPacketId()
{
return (PacketId)ReadUInt16();
}
/// <summary>
/// Returns a ushort without advancing the reader.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal PacketId PeekPacketId()
{
int currentPosition = Position;
PacketId result = ReadPacketId();
Position = currentPosition;
return result;
}
/// <summary>
/// Skips a number of bytes in the reader.
/// </summary>
/// <param name="value">Number of bytes to skip.</param>
[CodegenExclude]
public void Skip(int value)
{
if (value < 1 || Remaining < value)
return;
Position += value;
}
/// <summary>
/// Clears remaining bytes to be read.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
if (Remaining > 0)
Skip(Remaining);
}
/// <summary>
/// Returns the buffer as an ArraySegment.
/// </summary>
/// <returns></returns>
public ArraySegment<byte> GetArraySegmentBuffer()
{
return new ArraySegment<byte>(_buffer, Offset, Length);
}
/// <summary>
/// Returns the buffer as bytes. This does not trim excessive bytes.
/// </summary>
/// <returns></returns>
public byte[] GetByteBuffer()
{
return _buffer;
}
/// <summary>
/// Returns the buffer as bytes and allocates into a new array.
/// </summary>
/// <returns></returns>
public byte[] GetByteBufferAllocated()
{
byte[] result = new byte[Length];
Buffer.BlockCopy(_buffer, Offset, result, 0, Length);
return result;
}
/// <summary>
/// BlockCopies data from the reader to target and advances reader.
/// </summary>
/// <param name="target"></param>
/// <param name="targetOffset"></param>
/// <param name="count"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void BlockCopy(ref byte[] target, int targetOffset, int count)
{
Buffer.BlockCopy(_buffer, Position, target, targetOffset, count);
Position += count;
}
/// <summary>
/// Reads a byte.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
byte r = _buffer[Position];
Position += 1;
return r;
}
/// <summary>
/// Read bytes from position into target.
/// </summary>
/// <param name="buffer">Buffer to read bytes into.</param>
/// <param name="count">Number of bytes to read.</param>
[CodegenExclude]
public void ReadBytes(ref byte[] buffer, int count)
{
if (buffer == null)
throw new EndOfStreamException($"Target is null.");
//Target isn't large enough.
if (count > buffer.Length)
throw new EndOfStreamException($"Count of {count} exceeds target length of {buffer.Length}.");
BlockCopy(ref buffer, 0, count);
}
/// <summary>
/// Creates an ArraySegment by reading a number of bytes from position.
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArraySegment<byte> ReadArraySegment(int count)
{
ArraySegment<byte> result = new ArraySegment<byte>(_buffer, Position, count);
Position += count;
return result;
}
/// <summary>
/// Reads a sbyte.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sbyte ReadSByte()
{
return (sbyte)ReadByte();
}
/// <summary>
/// Reads a char.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public char ReadChar() => (char)ReadUInt16();
/// <summary>
/// Reads a boolean.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ReadBoolean()
{
byte result = ReadByte();
return (result == 1) ? true : false;
}
/// <summary>
/// Reads an int16.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ReadUInt16()
{
ushort result = 0;
result |= _buffer[Position++];
result |= (ushort)(_buffer[Position++] << 8);
return result;
}
/// <summary>
/// Reads a uint16.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short ReadInt16() => (short)ReadUInt16();
/// <summary>
/// Reads an int32.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ReadUInt32(AutoPackType packType = AutoPackType.Packed)
{
if (packType == AutoPackType.Packed)
return (uint)ReadPackedWhole();
uint result = 0;
result |= _buffer[Position++];
result |= (uint)_buffer[Position++] << 8;
result |= (uint)_buffer[Position++] << 16;
result |= (uint)_buffer[Position++] << 24;
return result;
}
/// <summary>
/// Reads a uint32.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadInt32(AutoPackType packType = AutoPackType.Packed)
{
if (packType == AutoPackType.Packed)
return (int)(long)ZigZagDecode(ReadPackedWhole());
return (int)ReadUInt32(packType);
}
/// <summary>
/// Reads a uint64.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long ReadInt64(AutoPackType packType = AutoPackType.Packed)
{
if (packType == AutoPackType.Packed)
return (long)ZigZagDecode(ReadPackedWhole());
return (long)ReadUInt64(packType);
}
/// <summary>
/// Reads an int64.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ReadUInt64(AutoPackType packType = AutoPackType.Packed)
{
if (packType == AutoPackType.Packed)
return (ulong)ReadPackedWhole();
ulong result = 0;
result |= _buffer[Position++];
result |= (ulong)_buffer[Position++] << 8;
result |= (ulong)_buffer[Position++] << 16;
result |= (ulong)_buffer[Position++] << 24;
result |= (ulong)_buffer[Position++] << 32;
result |= (ulong)_buffer[Position++] << 40;
result |= (ulong)_buffer[Position++] << 48;
result |= (ulong)_buffer[Position++] << 56;
return result;
}
/// <summary>
/// Reads a single.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float ReadSingle(AutoPackType packType = AutoPackType.Unpacked)
{
if (packType == AutoPackType.Unpacked)
{
UIntFloat converter = new UIntFloat();
converter.UIntValue = ReadUInt32(AutoPackType.Unpacked);
return converter.FloatValue;
}
else
{
long converter = (long)ReadPackedWhole();
return (float)(converter / 100f);
}
}
/// <summary>
/// Reads a double.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double ReadDouble()
{
UIntDouble converter = new UIntDouble();
converter.LongValue = ReadUInt64(AutoPackType.Unpacked);
return converter.DoubleValue;
}
/// <summary>
/// Reads a decimal.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public decimal ReadDecimal()
{
UIntDecimal converter = new UIntDecimal();
converter.LongValue1 = ReadUInt64(AutoPackType.Unpacked);
converter.LongValue2 = ReadUInt64(AutoPackType.Unpacked);
return converter.DecimalValue;
}
/// <summary>
/// Reads a string.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadString()
{
int size = ReadInt32();
//Null string.
if (size == Writer.UNSET_COLLECTION_SIZE_VALUE)
return null;
else if (size == 0)
return string.Empty;
if (!CheckAllocationAttack(size))
return string.Empty;
ArraySegment<byte> data = ReadArraySegment(size);
return ReaderStatics.GetString(data);
}
/// <summary>
/// Creates a byte array and reads bytes and size into it.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[] ReadBytesAndSizeAllocated()
{
int size = ReadInt32();
if (size == Writer.UNSET_COLLECTION_SIZE_VALUE)
return null;
else
return ReadBytesAllocated(size);
}
/// <summary>
/// Reads bytes and size and copies results into target. Returns UNSET if null was written.
/// </summary>
/// <returns>Bytes read.</returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadBytesAndSize(ref byte[] target)
{
int size = ReadInt32();
if (size > 0)
ReadBytes(ref target, size);
return size;
}
/// <summary>
/// Reads bytes and size and returns as an ArraySegment.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArraySegment<byte> ReadArraySegmentAndSize()
{
int size = ReadInt32();
/* UNSET would be written for null. But since
* ArraySegments cannot be null return default if
* length is unset or 0. */
if (size == Writer.UNSET_COLLECTION_SIZE_VALUE || size == 0)
return default;
return ReadArraySegment(size);
}
/// <summary>
/// Reads a Vector2.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector2 ReadVector2()
{
return new Vector2(ReadSingle(), ReadSingle());
}
/// <summary>
/// Reads a Vector3.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3 ReadVector3()
{
return new Vector3(ReadSingle(), ReadSingle(), ReadSingle());
}
/// <summary>
/// Reads a Vector4.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ReadVector4()
{
return new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
/// <summary>
/// Reads a Vector2Int.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector2Int ReadVector2Int(AutoPackType packType = AutoPackType.Packed)
{
return new Vector2Int(ReadInt32(packType), ReadInt32(packType));
}
/// <summary>
/// Reads a Vector3Int.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector3Int ReadVector3Int(AutoPackType packType = AutoPackType.Packed)
{
return new Vector3Int(ReadInt32(packType), ReadInt32(packType), ReadInt32(packType));
}
/// <summary>
/// Reads a color.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color ReadColor(AutoPackType packType = AutoPackType.Packed)
{
float r, g, b, a;
if (packType == AutoPackType.Unpacked)
{
r = ReadSingle();
g = ReadSingle();
b = ReadSingle();
a = ReadSingle();
}
else
{
r = (float)(ReadByte() / 100f);
g = (float)(ReadByte() / 100f);
b = (float)(ReadByte() / 100f);
a = (float)(ReadByte() / 100f);
}
return new Color(r, g, b, a);
}
/// <summary>
/// Reads a Color32.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color32 ReadColor32()
{
return new Color32(ReadByte(), ReadByte(), ReadByte(), ReadByte());
}
/// <summary>
/// Reads a Quaternion.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Quaternion ReadQuaternion(AutoPackType packType = AutoPackType.Packed)
{
if (packType == AutoPackType.Packed)
{
uint result = ReadUInt32(AutoPackType.Unpacked);
return Quaternion32Compression.Decompress(result);
}
else if (packType == AutoPackType.PackedLess)
{
ulong result = ReadUInt64(AutoPackType.Unpacked);
return Quaternion64Compression.Decompress(result);
}
else
{
return new Quaternion(
ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()
);
}
}
/// <summary>
/// Reads a Rect.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rect ReadRect()
{
return new Rect(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
}
/// <summary>
/// Plane.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Plane ReadPlane()
{
return new Plane(ReadVector3(), ReadSingle());
}
/// <summary>
/// Reads a Ray.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ray ReadRay()
{
Vector3 position = ReadVector3();
Vector3 direction = ReadVector3();
return new Ray(position, direction);
}
/// <summary>
/// Reads a Ray.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ray2D ReadRay2D()
{
Vector3 position = ReadVector2();
Vector2 direction = ReadVector2();
return new Ray2D(position, direction);
}
/// <summary>
/// Reads a Matrix4x4.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Matrix4x4 ReadMatrix4x4()
{
Matrix4x4 result = new Matrix4x4
{
m00 = ReadSingle(),
m01 = ReadSingle(),
m02 = ReadSingle(),
m03 = ReadSingle(),
m10 = ReadSingle(),
m11 = ReadSingle(),
m12 = ReadSingle(),
m13 = ReadSingle(),
m20 = ReadSingle(),
m21 = ReadSingle(),
m22 = ReadSingle(),
m23 = ReadSingle(),
m30 = ReadSingle(),
m31 = ReadSingle(),
m32 = ReadSingle(),
m33 = ReadSingle()
};
return result;
}
/// <summary>
/// Creates a new byte array and reads bytes into it.
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte[] ReadBytesAllocated(int count)
{
byte[] bytes = new byte[count];
ReadBytes(ref bytes, count);
return bytes;
}
/// <summary>
/// Reads a Guid.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public System.Guid ReadGuid()
{
byte[] buffer = ReaderStatics.GetGuidBuffer();
ReadBytes(ref buffer, 16);
return new System.Guid(buffer);
}
/// <summary>
/// Reads a GameObject.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GameObject ReadGameObject()
{
NetworkObject nob = ReadNetworkObject();
return (nob == null) ? null : nob.gameObject;
}
/// <summary>
/// Reads a Transform.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Transform ReadTransform()
{
NetworkObject nob = ReadNetworkObject();
return (nob == null) ? null : nob.transform;
}
/// <summary>
/// Reads a NetworkObject.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkObject ReadNetworkObject()
{
return ReadNetworkObject(out _);
}
/// <summary>
/// Reads a NetworkObject.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkObject ReadNetworkObject(out int objectOrPrefabId)
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
LastNetworkBehaviour = null;
#endif
objectOrPrefabId = ReadNetworkObjectId();
bool isSpawned;
/* UNSET indicates that the object
* is null or no PrefabId is set.
* PrefabIds are set in Awake within
* the NetworkManager so that should
* never happen so long as nob isn't null. */
if (objectOrPrefabId == NetworkObject.UNSET_OBJECTID_VALUE)
return null;
else
isSpawned = ReadBoolean();
bool isServer = NetworkManager.ServerManager.Started;
bool isClient = NetworkManager.ClientManager.Started;
NetworkObject result;
//Is spawned.
if (isSpawned)
{
result = null;
/* Try to get the object client side first if client
* is running. When acting as a host generally the object
* will be available in the server and client list
* but there can be occasions where the server side
* deinitializes the object, making it unavailable, while
* it is still available in the client side. Since FishNet doesn't
* use a fake host connection like some lesser solutions the client
* has to always be treated as it's own entity. */
if (isClient)
NetworkManager.ClientManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result);
//If not found on client and server is running then try server.
if (result == null && isServer)
NetworkManager.ServerManager.Objects.Spawned.TryGetValueIL2CPP(objectOrPrefabId, out result);
}
//Not spawned.
else
{
//Only look up asServer if not client, otherwise use client.
bool asServer = !isClient;
//Look up prefab.
result = NetworkManager.GetPrefab(objectOrPrefabId, asServer);
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
LastNetworkObject = result;
#endif
return result;
}
/// <summary>
/// Reads a NetworkObjectId and nothing else.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadNetworkObjectId()
{
return ReadUInt16();
}
/// <summary>
/// Reads the Id for a NetworkObject and outputs spawn settings.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int ReadNetworkObjectForSpawn(out sbyte initializeOrder, out ushort collectionid, out bool spawned)
{
int objectId = ReadNetworkObjectId();
bool isNull = (objectId == NetworkObject.UNSET_OBJECTID_VALUE);
if (isNull)
{
initializeOrder = 0;
collectionid = 0;
spawned = false;
}
else
{
collectionid = ReadUInt16();
initializeOrder = ReadSByte();
spawned = ReadBoolean();
}
return objectId;
}
/// <summary>
/// Reads the Id for a NetworkObject and outputs despawn settings.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int ReadNetworkObjectForDepawn(out DespawnType dt)
{
int objectId = ReadNetworkObjectId();
dt = (DespawnType)ReadByte();
return objectId;
}
/// <summary>
/// Reads a NetworkBehaviourId and ObjectId.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal byte ReadNetworkBehaviourId(out int objectId)
{
objectId = ReadNetworkObjectId();
if (objectId != NetworkObject.UNSET_OBJECTID_VALUE)
return ReadByte();
else
return 0;
}
/// <summary>
/// Reads a NetworkBehaviour.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkBehaviour ReadNetworkBehaviour(out int objectId, out byte componentIndex)
{
NetworkObject nob = ReadNetworkObject(out objectId);
componentIndex = ReadByte();
NetworkBehaviour result;
if (nob == null)
{
result = null;
}
else
{
if (componentIndex >= nob.NetworkBehaviours.Length)
{
NetworkManager.LogError($"ComponentIndex of {componentIndex} is out of bounds on {nob.gameObject.name} [id {nob.ObjectId}]. This may occur if you have modified your gameObject/prefab without saving it, or the scene.");
result = null;
}
else
{
result = nob.NetworkBehaviours[componentIndex];
}
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
LastNetworkBehaviour = result;
#endif
return result;
}
/// <summary>
/// Reads a NetworkBehaviour.
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkBehaviour ReadNetworkBehaviour()
{
return ReadNetworkBehaviour(out _, out _);
}
/// <summary>
/// Reads a DateTime.
/// </summary>
/// <param name="dt"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DateTime ReadDateTime()
{
DateTime result = DateTime.FromBinary(ReadInt64());
return result;
}
/// <summary>
/// Reads a transport channel.
/// </summary>
/// <param name="channel"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Channel ReadChannel()
{
return (Channel)ReadByte();
}
/// <summary>
/// Reads the Id for a NetworkConnection.
/// </summary>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadNetworkConnectionId()
{
return ReadInt16();
}
/// <summary>
/// Writes a NetworkConnection.
/// </summary>
/// <param name="conn"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetworkConnection ReadNetworkConnection()
{
int value = ReadNetworkConnectionId();
if (value == NetworkConnection.UNSET_CLIENTID_VALUE)
{
return FishNet.Managing.NetworkManager.EmptyConnection;
}
else
{
//Prefer server.
if (NetworkManager.IsServer)
{
NetworkConnection result;
if (NetworkManager.ServerManager.Clients.TryGetValueIL2CPP(value, out result))
{
return result;
}
//If also client then try client side data.
else if (NetworkManager.IsClient)
{
//If found in client collection then return.
if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out result))
return result;
/* Otherwise make a new instance.
* We do not know if this is for the server or client so
* initialize it either way. Connections rarely come through
* without being in server/client side collection. */
else
return new NetworkConnection(NetworkManager, value, true);
}
//Only server and not found.
else
{
NetworkManager.LogWarning($"Unable to find connection for read Id {value}. An empty connection will be returned.");
return FishNet.Managing.NetworkManager.EmptyConnection;
}
}
//Try client side, will only be able to fetch against local connection.
else
{
//If value is self then return self.
if (value == NetworkManager.ClientManager.Connection.ClientId)
return NetworkManager.ClientManager.Connection;
//Try client side dictionary.
else if (NetworkManager.ClientManager.Clients.TryGetValueIL2CPP(value, out NetworkConnection result))
return result;
/* Otherwise make a new instance.
* We do not know if this is for the server or client so
* initialize it either way. Connections rarely come through
* without being in server/client side collection. */
else
return new NetworkConnection(NetworkManager, value, true);
}
}
}
/// <summary>
/// Checks if the size could possibly be an allocation attack.
/// </summary>
/// <param name="size"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CheckAllocationAttack(int size)
{
/* Possible attacks. Impossible size, or size indicates
* more elements in collection or more bytes needed
* than what bytes are available. */
if (size != Writer.UNSET_COLLECTION_SIZE_VALUE && size < 0)
{
NetworkManager.LogError($"Size of {size} is invalid.");
return false;
}
if (size > Remaining)
{
NetworkManager.LogError($"Read size of {size} is larger than remaining data of {Remaining}.");
return false;
}
//Checks pass.
return true;
}
#region Packed readers.
/// <summary>
/// ZigZag decode an integer. Move the sign bit back to the left.
/// </summary>
public ulong ZigZagDecode(ulong value)
{
ulong sign = value << 63;
if (sign > 0)
return ~(value >> 1) | sign;
return value >> 1;
}
/// <summary>
/// Reads a packed whole number.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ReadPackedWhole()
{
byte data = ReadByte();
ulong result = (ulong)(data & 0x7F);
if ((data & 0x80) == 0) return result;
data = ReadByte();
result |= (ulong)(data & 0x7F) << 7;
if ((data & 0x80) == 0) return result;
data = ReadByte();
result |= (ulong)(data & 0x7F) << 14;
if ((data & 0x80) == 0) return result;
data = ReadByte();
result |= (ulong)(data & 0x7F) << 21;
if ((data & 0x80) == 0) return result;
data = ReadByte();
result |= (ulong)(data & 0x0F) << 28;
int extraBytes = data >> 4;
switch (extraBytes)
{
case 0:
break;
case 1:
result |= (ulong)ReadByte() << 32;
break;
case 2:
result |= (ulong)ReadByte() << 32;
result |= (ulong)ReadByte() << 40;
break;
case 3:
result |= (ulong)ReadByte() << 32;
result |= (ulong)ReadByte() << 40;
result |= (ulong)ReadByte() << 48;
break;
case 4:
result |= (ulong)ReadByte() << 32;
result |= (ulong)ReadByte() << 40;
result |= (ulong)ReadByte() << 48;
result |= (ulong)ReadByte() << 56;
break;
}
return result;
}
#endregion
#region Generators.
/// <summary>
/// Reads a replicate into collection and returns item count read.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int ReadReplicate<T>(ref T[] collection, uint tick) where T : IReplicateData
{
//Number of entries written.
int count = (int)ReadByte();
if (collection == null || collection.Length < count)
collection = new T[count];
/* Subtract count total minus 1
* from starting tick. This sets the tick to what the first entry would be.
* EG packet came in as tick 100, so that was passed as tick.
* if there are 3 replicates then 2 would be subtracted (count - 1).
* The new tick would be 98.
* Ticks would be assigned to read values from oldest to
* newest as 98, 99, 100. Which is the correct result. In order for this to
* work properly past replicates cannot skip ticks. This will be ensured
* in another part of the code. */
tick -= (uint)(count - 1);
int fullPackType = ReadByte();
//Read once and apply to all entries.
if (fullPackType > 0)
{
T value;
if (fullPackType == Writer.REPLICATE_ALL_DEFAULT_BYTE)
{
value = default(T);
}
else if (fullPackType == Writer.REPLICATE_REPEATING_BYTE)
{
value = Read<T>();
}
else
{
value = default(T);
NetworkManager?.LogError($"Unhandled Replicate pack type {fullPackType}.");
}
for (int i = 0; i < count; i++)
{
collection[i] = value;
collection[i].SetTick(tick + (uint)i);
}
}
//Values vary, read each indicator.
else
{
T lastData = default;
for (int i = 0; i < count; i++)
{
T value = default;
byte indicatorB = ReadByte();
if (indicatorB == Writer.REPLICATE_DUPLICATE_BYTE)
{
value = lastData;
}
else if (indicatorB == Writer.REPLICATE_UNIQUE_BYTE)
{
value = Read<T>();
lastData = value;
}
else if (indicatorB == Writer.REPLICATE_DEFAULT_BYTE)
{
value = default(T);
}
//Apply tick.
value.SetTick(tick + (uint)i);
//Assign to collection.
collection[i] = value;
}
}
return count;
}
/// <summary>
/// Reads a ListCache with allocations.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ListCache<T> ReadListCacheAllocated<T>()
{
List<T> lst = ReadListAllocated<T>();
ListCache<T> lc = new ListCache<T>();
lc.Collection = lst;
return lc;
}
/// <summary>
/// Reads a ListCache and returns the item count read.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadListCache<T>(ref ListCache<T> listCache)
{
listCache.Collection = ReadListAllocated<T>();
return listCache.Collection.Count;
}
/// <summary>
/// Reads a list with allocations.
/// </summary>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<T> ReadListAllocated<T>()
{
List<T> result = null;
ReadList<T>(ref result);
return result;
}
/// <summary>
/// Reads into collection and returns item count read.
/// </summary>
/// <param name="collection"></param>
/// <param name="allowNullification">True to allow the referenced collection to be nullified when receiving a null collection read.</param>
/// <returns>Number of values read into the collection. UNSET is returned if the collection were read as null.</returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadList<T>(ref List<T> collection, bool allowNullification = false)
{
int count = ReadInt32();
if (count == Writer.UNSET_COLLECTION_SIZE_VALUE)
{
if (allowNullification)
collection = null;
return Writer.UNSET_COLLECTION_SIZE_VALUE;
}
else
{
if (collection == null)
collection = new List<T>(count);
else
collection.Clear();
for (int i = 0; i < count; i++)
collection.Add(Read<T>());
return count;
}
}
/// <summary>
/// Reads an array.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] ReadArrayAllocated<T>()
{
T[] result = null;
ReadArray<T>(ref result);
return result;
}
/// <summary>
/// Reads into collection and returns amount read.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadArray<T>(ref T[] collection)
{
int count = ReadInt32();
if (count == Writer.UNSET_COLLECTION_SIZE_VALUE)
{
return 0;
}
else if (count == 0)
{
if (collection == null)
collection = new T[0];
return 0;
}
else
{
//Initialize buffer if not already done.
if (collection == null)
collection = new T[count];
else if (collection.Length < count)
Array.Resize(ref collection, count);
for (int i = 0; i < count; i++)
collection[i] = Read<T>();
return count;
}
}
/// <summary>
/// Reads any supported type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[CodegenExclude]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Read<T>()
{
System.Type type = typeof(T);
if (IsAutoPackType(type, out AutoPackType packType))
{
Func<Reader, AutoPackType, T> autopackDel = GenericReader<T>.ReadAutoPack;
if (autopackDel == null)
{
LogError(GetLogMessage());
return default;
}
else
{
return autopackDel.Invoke(this, packType);
}
}
else
{
Func<Reader, T> del = GenericReader<T>.Read;
if (del == null)
{
LogError(GetLogMessage());
return default;
}
else
{
return del.Invoke(this);
}
}
string GetLogMessage() => $"Read method not found for {type.FullName}. Use a supported type or create a custom serializer.";
}
/// <summary>
/// Logs an error.
/// </summary>
/// <param name="msg"></param>
private void LogError(string msg)
{
if (NetworkManager == null)
NetworkManager.StaticLogError(msg);
else
NetworkManager.LogError(msg);
}
/// <summary>
/// Returns if T takes AutoPackType argument.
/// </summary>
/// <param name="packType">Outputs the default pack type for T.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool IsAutoPackType<T>(out AutoPackType packType) => Writer.IsAutoPackType<T>(out packType);
/// <summary>
/// Returns if T takes AutoPackType argument.
/// </summary>
/// <param name="packType">Outputs the default pack type for T.</param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool IsAutoPackType(Type type, out AutoPackType packType) => Writer.IsAutoPackType(type, out packType);
#endregion
}
}