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
///
/// Used to insert length for delta flags.
///
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.
///
/// Writes a delta value.
///
/// True if written.
[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.
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaInt8(sbyte valueA, sbyte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
///
/// Writes a delta value.
///
/// True if written.
public bool WriteDeltaUInt8(byte valueA, byte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaInt16(short valueA, short valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaUInt16(ushort valueA, ushort valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaInt32(int valueA, int valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaUInt32(uint valueA, uint valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[DefaultDeltaWriter]
public bool WriteDeltaInt64(long valueA, long valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDeltaUInt64((ulong)valueA, (ulong)valueB, option);
///
/// Writes a delta value.
///
/// True if written.
[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;
}
///
/// Writes the difference between two values for signed and unsigned shorts and ints.
///
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.
///
/// Writes a delta value.
///
/// True if written.
[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;
}
///
/// Writes a delta value using a compression type.
///
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);
}
}
///
/// Returns DeltaPrecisionType for the difference of two values.
/// Value returned should be written as signed.
///
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);
}
///
/// Returns DeltaPrecisionType for the difference of two values.
///
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;
}
///
/// Returns DeltaPrecisionType for a value.
///
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.
///
/// Writes a delta value.
///
/// True if written.
[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;
}
///
/// Writes a double using DeltaPrecisionType.
///
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}.");
}
}
///
/// Returns DeltaPrecisionType for the difference of two values.
///
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);
}
///
/// Returns DeltaPrecisionType for the difference of two values.
///
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;
}
///
/// Returns DeltaPrecisionType for a value.
///
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
///
/// Writes a delta value.
///
/// True if written.
[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;
}
///
/// Writes a double using DeltaPrecisionType.
///
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}.");
}
}
///
/// Returns DeltaPrecisionType for the difference of two values.
///
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);
}
///
/// Returns DeltaPrecisionType for the difference of two values.
///
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;
}
///
/// Returns DeltaPrecisionType for a value.
///
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.
///
/// Writes a delta value.
///
/// True if written.
[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.
///
/// Writes delta position, rotation, and scale of a transform.
///
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;
}
}
///
/// Writes a delta quaternion.
///
[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;
}
///
/// Returns if quaternion values differ.
///
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;
}
///
/// Writes a delta value.
///
[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;
}
///
/// Writes a delta value.
///
//[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.
///
/// Writes a delta reconcile.
///
internal void WriteDeltaReconcile(T lastReconcile, T value, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDelta(lastReconcile, value, option);
///
/// Writes a delta replicate using a list.
///
internal void WriteDeltaReplicate(List 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;
}
}
///
/// Writes a delta replicate using a BasicQueue.
///
internal void WriteDeltaReplicate(BasicQueue 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 prev, T next, DeltaSerializerOption option = DeltaSerializerOption.Unset)
{
Func del = GenericDeltaWriter.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
}
}