mirror of
https://github.com/Dadechin/Unity-WebSocket.git
synced 2025-07-03 20:04:33 +00:00
830 lines
32 KiB
C#
830 lines
32 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using FishNet.CodeGenerating;
|
|
using System.Runtime.CompilerServices;
|
|
using FishNet.Component.Transforming;
|
|
using FishNet.Managing;
|
|
using FishNet.Object;
|
|
using FishNet.Object.Prediction;
|
|
using FishNet.Serializing.Helping;
|
|
using GameKit.Dependencies.Utilities;
|
|
using UnityEngine;
|
|
|
|
namespace FishNet.Serializing
|
|
{
|
|
public partial class Writer
|
|
{
|
|
#region Types.
|
|
[System.Flags]
|
|
internal enum UnsignedVector3DeltaFlag : int
|
|
{
|
|
Unset = 0,
|
|
More = (1 << 0),
|
|
X1 = (1 << 1),
|
|
NextXIsLarger = (1 << 2),
|
|
Y1 = (1 << 3),
|
|
NextYIsLarger = (1 << 4),
|
|
Z1 = (1 << 5),
|
|
NextZIsLarger = (1 << 6),
|
|
X2 = (1 << 8),
|
|
X4 = (1 << 9),
|
|
Y2 = (1 << 10),
|
|
Y4 = (1 << 11),
|
|
Z2 = (1 << 12),
|
|
Z4 = (1 << 13),
|
|
}
|
|
#endregion
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Used to insert length for delta flags.
|
|
/// </summary>
|
|
private ReservedLengthWriter _reservedLengthWriter = new();
|
|
|
|
private const double LARGEST_DELTA_PRECISION_INT8 = (sbyte.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_INT16 = (short.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_INT32 = (int.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_INT64 = (long.MaxValue / DOUBLE_ACCURACY);
|
|
|
|
private const double LARGEST_DELTA_PRECISION_UINT8 = (byte.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_UINT16 = (ushort.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_UINT32 = (uint.MaxValue / DOUBLE_ACCURACY);
|
|
private const double LARGEST_DELTA_PRECISION_UINT64 = (ulong.MaxValue / DOUBLE_ACCURACY);
|
|
internal const double DOUBLE_ACCURACY = 1000d;
|
|
internal const double DOUBLE_ACCURACY_PRECISION = (1f / DOUBLE_ACCURACY);
|
|
internal const decimal DECIMAL_ACCURACY = 1000m;
|
|
|
|
internal const float QUATERNION_PRECISION = 0.0001f;
|
|
|
|
#region Other.
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaBoolean(bool valueA, bool valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
bool valuesMatch = (valueA == valueB);
|
|
if (valuesMatch && option == DeltaSerializerOption.Unset)
|
|
return false;
|
|
|
|
WriteBoolean(valueB);
|
|
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
#region Whole values.
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaInt8(sbyte valueA, sbyte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
public bool WriteDeltaUInt8(byte valueA, byte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaInt16(short valueA, short valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaUInt16(ushort valueA, ushort valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaInt32(int valueA, int valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaUInt32(uint valueA, uint valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaInt64(long valueA, long valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDeltaUInt64((ulong)valueA, (ulong)valueB, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaUInt64(ulong valueA, ulong valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
bool unchangedValue = (valueA == valueB);
|
|
if (unchangedValue && option == DeltaSerializerOption.Unset) return false;
|
|
|
|
bool bLargerThanA = (valueB > valueA);
|
|
ulong next = (bLargerThanA) ? (valueB - valueA) : (valueA - valueB);
|
|
|
|
WriteBoolean(bLargerThanA);
|
|
WriteUnsignedPackedWhole(next);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the difference between two values for signed and unsigned shorts and ints.
|
|
/// </summary>
|
|
private bool WriteDifference8_16_32(long valueA, long valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
bool unchangedValue = (valueA == valueB);
|
|
if (unchangedValue && option == DeltaSerializerOption.Unset) return false;
|
|
|
|
long next = (valueB - valueA);
|
|
WriteSignedPackedWhole(next);
|
|
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
#region Single.
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteUDeltaSingle(float valueA, float valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out float unsignedDifference);
|
|
|
|
if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset)
|
|
return false;
|
|
|
|
WriteUInt8Unpacked((byte)dpt);
|
|
WriteDeltaSingle(dpt, unsignedDifference, unsigned: true);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a delta value using a compression type.
|
|
/// </summary>
|
|
private void WriteDeltaSingle(UDeltaPrecisionType dpt, float value, bool unsigned)
|
|
{
|
|
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt8Unpacked((byte)Math.Floor(value * DOUBLE_ACCURACY));
|
|
else
|
|
WriteInt8Unpacked((sbyte)Math.Floor(value * DOUBLE_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt16Unpacked((ushort)Math.Floor(value * DOUBLE_ACCURACY));
|
|
else
|
|
WriteInt16Unpacked((short)Math.Floor(value * DOUBLE_ACCURACY));
|
|
}
|
|
//Anything else is unpacked.
|
|
else
|
|
{
|
|
WriteSingleUnpacked(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// Value returned should be written as signed.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetSDeltaPrecisionType(float valueA, float valueB, out float signedDifference)
|
|
{
|
|
signedDifference = (valueB - valueA);
|
|
float posValue = (signedDifference < 0f) ? (signedDifference * -1f) : signedDifference;
|
|
|
|
return GetDeltaPrecisionType(posValue, unsigned: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetUDeltaPrecisionType(float valueA, float valueB, out float unsignedDifference)
|
|
{
|
|
bool bIsLarger = (valueB > valueA);
|
|
if (bIsLarger)
|
|
unsignedDifference = (valueB - valueA);
|
|
else
|
|
unsignedDifference = (valueA - valueB);
|
|
|
|
UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true);
|
|
//If result is set then set if bIsLarger.
|
|
if (bIsLarger && result != UDeltaPrecisionType.Unset)
|
|
result |= UDeltaPrecisionType.NextValueIsLarger;
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for a value.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetDeltaPrecisionType(float positiveValue, bool unsigned)
|
|
{
|
|
if (unsigned)
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< (float)DOUBLE_ACCURACY_PRECISION => UDeltaPrecisionType.Unset,
|
|
< (float)LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8,
|
|
< (float)LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16,
|
|
< (float)LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< (float)(DOUBLE_ACCURACY_PRECISION / 2d) => UDeltaPrecisionType.Unset,
|
|
< (float)LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8,
|
|
< (float)LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16,
|
|
< (float)LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Double.
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteUDeltaDouble(double valueA, double valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out double positiveDifference);
|
|
|
|
if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset) return false;
|
|
|
|
WriteUInt8Unpacked((byte)dpt);
|
|
WriteDeltaDouble(dpt, positiveDifference, unsigned: true);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a double using DeltaPrecisionType.
|
|
/// </summary>
|
|
private void WriteDeltaDouble(UDeltaPrecisionType dpt, double value, bool unsigned)
|
|
{
|
|
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt8Unpacked((byte)Math.Floor(value * DOUBLE_ACCURACY));
|
|
else
|
|
WriteInt8Unpacked((sbyte)Math.Floor(value * DOUBLE_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt16Unpacked((ushort)Math.Floor(value * DOUBLE_ACCURACY));
|
|
else
|
|
WriteInt16Unpacked((short)Math.Floor(value * DOUBLE_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt32))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt32Unpacked((uint)Math.Floor(value * DOUBLE_ACCURACY));
|
|
else
|
|
WriteInt32Unpacked((int)Math.Floor(value * DOUBLE_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.Unset))
|
|
{
|
|
WriteDoubleUnpacked(value);
|
|
}
|
|
else
|
|
{
|
|
NetworkManagerExtensions.LogError($"Unhandled precision type of {dpt}.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetSDeltaPrecisionType(double valueA, double valueB, out double signedDifference)
|
|
{
|
|
signedDifference = (valueB - valueA);
|
|
double posValue = (signedDifference < 0d) ? (signedDifference * -1d) : signedDifference;
|
|
|
|
return GetDeltaPrecisionType(posValue, unsigned: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetUDeltaPrecisionType(double valueA, double valueB, out double unsignedDifference)
|
|
{
|
|
bool bIsLarger = (valueB > valueA);
|
|
if (bIsLarger)
|
|
unsignedDifference = (valueB - valueA);
|
|
else
|
|
unsignedDifference = (valueA - valueB);
|
|
|
|
UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true);
|
|
if (bIsLarger && result != UDeltaPrecisionType.Unset)
|
|
result |= UDeltaPrecisionType.NextValueIsLarger;
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for a value.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetDeltaPrecisionType(double positiveValue, bool unsigned)
|
|
{
|
|
if (unsigned)
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8,
|
|
< LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16,
|
|
< LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8,
|
|
< LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16,
|
|
< LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Decimal
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteUDeltaDecimal(decimal valueA, decimal valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out decimal positiveDifference);
|
|
|
|
if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset) return false;
|
|
|
|
WriteUInt8Unpacked((byte)dpt);
|
|
WriteDeltaDecimal(dpt, positiveDifference, unsigned: true);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a double using DeltaPrecisionType.
|
|
/// </summary>
|
|
private void WriteDeltaDecimal(UDeltaPrecisionType dpt, decimal value, bool unsigned)
|
|
{
|
|
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt8Unpacked((byte)Math.Floor(value * DECIMAL_ACCURACY));
|
|
else
|
|
WriteInt8Unpacked((sbyte)Math.Floor(value * DECIMAL_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt16Unpacked((ushort)Math.Floor(value * DECIMAL_ACCURACY));
|
|
else
|
|
WriteInt16Unpacked((short)Math.Floor(value * DECIMAL_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt32))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt32Unpacked((uint)Math.Floor(value * DECIMAL_ACCURACY));
|
|
else
|
|
WriteInt32Unpacked((int)Math.Floor(value * DECIMAL_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.UInt64))
|
|
{
|
|
if (unsigned)
|
|
WriteUInt64Unpacked((ulong)Math.Floor(value * DECIMAL_ACCURACY));
|
|
else
|
|
WriteInt64Unpacked((long)Math.Floor(value * DECIMAL_ACCURACY));
|
|
}
|
|
else if (dpt.FastContains(UDeltaPrecisionType.Unset))
|
|
{
|
|
WriteDecimalUnpacked(value);
|
|
}
|
|
else
|
|
{
|
|
NetworkManagerExtensions.LogError($"Unhandled precision type of {dpt}.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetSDeltaPrecisionType(decimal valueA, decimal valueB, out decimal signedDifference)
|
|
{
|
|
signedDifference = (valueB - valueA);
|
|
decimal posValue = (signedDifference < 0m) ? (signedDifference * -1m) : signedDifference;
|
|
|
|
return GetDeltaPrecisionType(posValue, unsigned: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for the difference of two values.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetUDeltaPrecisionType(decimal valueA, decimal valueB, out decimal unsignedDifference)
|
|
{
|
|
bool bIsLarger = (valueB > valueA);
|
|
if (bIsLarger)
|
|
unsignedDifference = (valueB - valueA);
|
|
else
|
|
unsignedDifference = (valueA - valueB);
|
|
|
|
UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true);
|
|
if (bIsLarger && result != UDeltaPrecisionType.Unset)
|
|
result |= UDeltaPrecisionType.NextValueIsLarger;
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns DeltaPrecisionType for a value.
|
|
/// </summary>
|
|
public UDeltaPrecisionType GetDeltaPrecisionType(decimal positiveValue, bool unsigned)
|
|
{
|
|
if (unsigned)
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< (decimal)LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8,
|
|
< (decimal)LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16,
|
|
< (decimal)LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32,
|
|
< (decimal)LARGEST_DELTA_PRECISION_UINT64 => UDeltaPrecisionType.UInt64,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return positiveValue switch
|
|
{
|
|
< (decimal)LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8,
|
|
< (decimal)LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16,
|
|
< (decimal)LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32,
|
|
< (decimal)LARGEST_DELTA_PRECISION_INT64 => UDeltaPrecisionType.UInt64,
|
|
_ => UDeltaPrecisionType.Unset,
|
|
};
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region FishNet Types.
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
/// <returns>True if written.</returns>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaNetworkBehaviour(NetworkBehaviour valueA, NetworkBehaviour valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
bool unchangedValue = (valueA == valueB);
|
|
if (unchangedValue && option == DeltaSerializerOption.Unset) return false;
|
|
|
|
WriteNetworkBehaviour(valueB);
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
#region Unity.
|
|
/// <summary>
|
|
/// Writes delta position, rotation, and scale of a transform.
|
|
/// </summary>
|
|
public bool WriteDeltaTransformProperties(TransformProperties valueA, TransformProperties valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
int startPosition = Position;
|
|
Skip(1);
|
|
|
|
byte allFlags = 0;
|
|
|
|
if (WriteDeltaVector3(valueA.Position, valueB.Position))
|
|
allFlags |= 1;
|
|
if (WriteDeltaQuaternion(valueA.Rotation, valueB.Rotation))
|
|
allFlags |= 2;
|
|
if (WriteDeltaVector3(valueA.Scale, valueB.Scale))
|
|
allFlags |= 4;
|
|
|
|
if (allFlags != 0 || option != DeltaSerializerOption.Unset)
|
|
{
|
|
InsertUInt8Unpacked(allFlags, startPosition);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Position = startPosition;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a delta quaternion.
|
|
/// </summary>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaQuaternion(Quaternion valueA, Quaternion valueB, float precision = QUATERNION_PRECISION, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
bool changed = (option != DeltaSerializerOption.Unset || IsQuaternionChanged(valueA, valueB));
|
|
|
|
if (!changed)
|
|
return false;
|
|
|
|
QuaternionDeltaPrecisionCompression.Compress(this, valueA, valueB, precision);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns if quaternion values differ.
|
|
/// </summary>
|
|
private bool IsQuaternionChanged(Quaternion valueA, Quaternion valueB)
|
|
{
|
|
const float minimumChange = 0.0025f;
|
|
|
|
if (Mathf.Abs(valueA.x - valueB.x) > minimumChange)
|
|
return true;
|
|
else if (Mathf.Abs(valueA.y - valueB.y) > minimumChange)
|
|
return true;
|
|
else if (Mathf.Abs(valueA.z - valueB.z) > minimumChange)
|
|
return true;
|
|
else if (Mathf.Abs(valueA.w - valueB.w) > minimumChange)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaVector2(Vector2 valueA, Vector2 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
//TODO Fit as many flags into a byte as possible for pack levels of each axis rather than 1 per axis.
|
|
byte allFlags = 0;
|
|
|
|
int startPosition = Position;
|
|
Skip(1);
|
|
|
|
if (WriteUDeltaSingle(valueA.x, valueB.x))
|
|
allFlags += 1;
|
|
if (WriteUDeltaSingle(valueA.y, valueB.y))
|
|
allFlags += 2;
|
|
|
|
if (allFlags != 0 || option != DeltaSerializerOption.Unset)
|
|
{
|
|
InsertUInt8Unpacked(allFlags, startPosition);
|
|
return true;
|
|
}
|
|
|
|
Position = startPosition;
|
|
return false;
|
|
}
|
|
|
|
[DefaultDeltaWriter]
|
|
public bool WriteDeltaVector3(Vector3 valueA, Vector3 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
//TODO Fit as many flags into a byte as possible for pack levels of each axis rather than 1 per axis.
|
|
byte allFlags = 0;
|
|
|
|
int startPosition = Position;
|
|
Skip(1);
|
|
|
|
if (WriteUDeltaSingle(valueA.x, valueB.x))
|
|
allFlags += 1;
|
|
if (WriteUDeltaSingle(valueA.y, valueB.y))
|
|
allFlags += 2;
|
|
if (WriteUDeltaSingle(valueA.z, valueB.z))
|
|
allFlags += 4;
|
|
|
|
if (allFlags != 0 || option != DeltaSerializerOption.Unset)
|
|
{
|
|
InsertUInt8Unpacked(allFlags, startPosition);
|
|
return true;
|
|
}
|
|
|
|
Position = startPosition;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a delta value.
|
|
/// </summary>
|
|
//[DefaultDeltaWriter]
|
|
public bool WriteDeltaVector3_New(Vector3 valueA, Vector3 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
UnsignedVector3DeltaFlag flags = UnsignedVector3DeltaFlag.Unset;
|
|
|
|
//Get precision type and out values.
|
|
UDeltaPrecisionType xDpt = GetUDeltaPrecisionType(valueA.x, valueB.x, out float xUnsignedDifference);
|
|
UDeltaPrecisionType yDpt = GetUDeltaPrecisionType(valueA.y, valueB.y, out float yUnsignedDifference);
|
|
UDeltaPrecisionType zDpt = GetUDeltaPrecisionType(valueA.z, valueB.z, out float zUnsignedDifference);
|
|
|
|
byte unsetDpt = (byte)UDeltaPrecisionType.Unset;
|
|
bool flagsAreUnset = ((byte)xDpt == unsetDpt && (byte)yDpt > unsetDpt && (byte)zDpt > unsetDpt);
|
|
|
|
//No change, can exit early.
|
|
if (flagsAreUnset && option == DeltaSerializerOption.Unset)
|
|
return false;
|
|
|
|
//No change but must write there's no change.
|
|
if (flagsAreUnset && option != DeltaSerializerOption.Unset)
|
|
{
|
|
WriteUInt8Unpacked((byte)UnsignedVector3DeltaFlag.Unset);
|
|
return true;
|
|
}
|
|
|
|
/* If here there is change. */
|
|
int startPosition = Position;
|
|
|
|
/* If x, y, or z dpt doesn't contain uint8 then it must contain a higher value.
|
|
* We already exited early if all values were unset, so there's no reason to
|
|
* check for unset here. */
|
|
bool areFlagsMultipleBytes = (!xDpt.FastContains(UDeltaPrecisionType.UInt8) || !yDpt.FastContains(UDeltaPrecisionType.UInt8) || !zDpt.FastContains(UDeltaPrecisionType.UInt8));
|
|
|
|
if (areFlagsMultipleBytes)
|
|
{
|
|
Skip(2);
|
|
flags |= UnsignedVector3DeltaFlag.More;
|
|
}
|
|
else
|
|
{
|
|
Skip(1);
|
|
}
|
|
|
|
//Write X.
|
|
if (xDpt != UDeltaPrecisionType.Unset)
|
|
{
|
|
flags |= GetShiftedFlag(xDpt, shift: 0);
|
|
WriteDeltaSingle(xDpt, xUnsignedDifference, unsigned: true);
|
|
}
|
|
|
|
//Write Y.
|
|
if (yDpt != UDeltaPrecisionType.Unset)
|
|
{
|
|
flags |= GetShiftedFlag(yDpt, shift: 2);
|
|
WriteDeltaSingle(yDpt, yUnsignedDifference, unsigned: true);
|
|
}
|
|
|
|
//Write Z.
|
|
if (zDpt != UDeltaPrecisionType.Unset)
|
|
{
|
|
flags |= GetShiftedFlag(zDpt, shift: 4);
|
|
WriteDeltaSingle(zDpt, zUnsignedDifference, unsigned: true);
|
|
}
|
|
|
|
//Returns flags to add onto delta flags using precisionType and shift.
|
|
UnsignedVector3DeltaFlag GetShiftedFlag(UDeltaPrecisionType precisionType, int shift)
|
|
{
|
|
int result;
|
|
if (precisionType.FastContains(UDeltaPrecisionType.UInt8))
|
|
{
|
|
result = ((int)UnsignedVector3DeltaFlag.X1 << shift);
|
|
// Debug.Log($"Axes {axes}. X1 {(int)UnsignedVector3DeltaFlag.X1}. Shifted {result}. Shift {shift}.");
|
|
}
|
|
else if (precisionType.FastContains(UDeltaPrecisionType.UInt16))
|
|
result = ((int)UnsignedVector3DeltaFlag.X2 << shift);
|
|
else
|
|
result = ((int)UnsignedVector3DeltaFlag.X4 << shift);
|
|
|
|
if (precisionType.FastContains(UDeltaPrecisionType.NextValueIsLarger))
|
|
result |= ((int)UnsignedVector3DeltaFlag.NextXIsLarger << shift);
|
|
|
|
return (UnsignedVector3DeltaFlag)result;
|
|
}
|
|
|
|
/* Do another check for if one byte or two, then write flags. */
|
|
|
|
//Multiple bytes.
|
|
if (areFlagsMultipleBytes)
|
|
{
|
|
int flagsValue = (int)flags;
|
|
|
|
int firstByte = (flagsValue & 0xff);
|
|
InsertUInt8Unpacked((byte)firstByte, startPosition);
|
|
int secondByte = (flagsValue >> 8);
|
|
InsertUInt8Unpacked((byte)secondByte, startPosition + 1);
|
|
}
|
|
//One byte.
|
|
else
|
|
{
|
|
InsertUInt8Unpacked((byte)flags, startPosition);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
#region Prediction.
|
|
/// <summary>
|
|
/// Writes a delta reconcile.
|
|
/// </summary>
|
|
internal void WriteDeltaReconcile<T>(T lastReconcile, T value, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDelta(lastReconcile, value, option);
|
|
|
|
/// <summary>
|
|
/// Writes a delta replicate using a list.
|
|
/// </summary>
|
|
internal void WriteDeltaReplicate<T>(List<T> values, int offset, DeltaSerializerOption option = DeltaSerializerOption.Unset) where T : IReplicateData
|
|
{
|
|
int collectionCount = values.Count;
|
|
//Replicate list will never be null, no need to write null check.
|
|
//Number of entries being written.
|
|
byte count = (byte)(collectionCount - offset);
|
|
WriteUInt8Unpacked(count);
|
|
|
|
T prev;
|
|
//Set previous if not full and if enough room in the collection to go back.
|
|
if (option != DeltaSerializerOption.FullSerialize && collectionCount > count)
|
|
prev = values[offset - 1];
|
|
else
|
|
prev = default;
|
|
|
|
for (int i = offset; i < collectionCount; i++)
|
|
{
|
|
T v = values[i];
|
|
WriteDelta(prev, v, option);
|
|
|
|
prev = v;
|
|
//After the first loop the deltaOption can be set to root, if not already.
|
|
option = DeltaSerializerOption.RootSerialize;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes a delta replicate using a BasicQueue.
|
|
/// </summary>
|
|
internal void WriteDeltaReplicate<T>(BasicQueue<T> values, int redundancyCount, DeltaSerializerOption option = DeltaSerializerOption.Unset) where T : IReplicateData
|
|
{
|
|
int collectionCount = values.Count;
|
|
//Replicate list will never be null, no need to write null check.
|
|
//Number of entries being written.
|
|
byte count = (byte)redundancyCount;
|
|
WriteUInt8Unpacked(count);
|
|
|
|
int offset = (collectionCount - redundancyCount);
|
|
T prev;
|
|
//Set previous if not full and if enough room in the collection to go back.
|
|
if (option != DeltaSerializerOption.FullSerialize && collectionCount > count)
|
|
prev = values[offset - 1];
|
|
else
|
|
prev = default;
|
|
|
|
for (int i = offset; i < collectionCount; i++)
|
|
{
|
|
T v = values[i];
|
|
WriteDelta(prev, v, option);
|
|
|
|
prev = v;
|
|
//After the first loop the deltaOption can be set to root, if not already.
|
|
option = DeltaSerializerOption.RootSerialize;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Generic.
|
|
public bool WriteDelta<T>(T prev, T next, DeltaSerializerOption option = DeltaSerializerOption.Unset)
|
|
{
|
|
Func<Writer, T, T, DeltaSerializerOption, bool> del = GenericDeltaWriter<T>.Write;
|
|
|
|
if (del == null)
|
|
{
|
|
NetworkManager.LogError($"Write delta method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer.");
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return del.Invoke(this, prev, next, option);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
} |