[Add] FishNet
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// How to pack data when using serialization.
|
||||
/// </summary>
|
||||
public enum AutoPackType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Data will not be compressed.
|
||||
/// </summary>
|
||||
Unpacked = 0,
|
||||
/// <summary>
|
||||
/// Data will be compressed to use the least amount of data possible.
|
||||
/// </summary>
|
||||
Packed = 1,
|
||||
/// <summary>
|
||||
/// Data will be compressed but not as much as Packed.
|
||||
/// </summary>
|
||||
PackedLess = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e94ebaa8f7024845a7e90ebd8246ac6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/AutoPackType.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
[System.Flags]
|
||||
public enum DeltaSerializerOption : ulong
|
||||
{
|
||||
Unset = 0,
|
||||
FullSerialize = 1,
|
||||
RootSerialize = 2
|
||||
}
|
||||
|
||||
public static class DeltaSerializerOptionExtensions
|
||||
{
|
||||
public static bool FastContains(this DeltaSerializerOption whole, DeltaSerializerOption part) => (whole & part) == part;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66c970b59e4d53249bfed364df2cf1db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,181 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// This is for internal use and may change at any time.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
public enum DeltaVector2Type : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// This is unused.
|
||||
/// </summary>
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// Contains X as 1 byte.
|
||||
/// </summary>
|
||||
XUInt8 = 1,
|
||||
/// <summary>
|
||||
/// Contains X as 2 bytes.
|
||||
/// </summary>
|
||||
XUInt16 = 2,
|
||||
/// <summary>
|
||||
/// Contains X as 4 bytes.
|
||||
/// </summary>
|
||||
XUInt32 = 4,
|
||||
/// <summary>
|
||||
/// Contains Z as 1 byte.
|
||||
/// </summary>
|
||||
YUInt8 = 8,
|
||||
/// <summary>
|
||||
/// Contains Z as 2 bytes.
|
||||
/// </summary>
|
||||
YUInt16 = 16,
|
||||
/// <summary>
|
||||
/// Contains Z as 4 bytes.
|
||||
/// </summary>
|
||||
YUInt32 = 32,
|
||||
/// <summary>
|
||||
/// Contains Y as 1 byte.
|
||||
/// </summary>
|
||||
XNextIsLarger = 64,
|
||||
/// <summary>
|
||||
/// Contains Y as 4 bytes.
|
||||
/// </summary>
|
||||
YNextIsLarger = 128
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is for internal use and may change at any time.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
public enum DeltaVector3Type : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// This is unused.
|
||||
/// </summary>
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// Contains X as 1 byte.
|
||||
/// </summary>
|
||||
XInt8 = 1,
|
||||
/// <summary>
|
||||
/// Contains X as 2 bytes.
|
||||
/// </summary>
|
||||
XInt16 = 2,
|
||||
/// <summary>
|
||||
/// Contains X as 4 bytes.
|
||||
/// </summary>
|
||||
XInt32 = 4,
|
||||
/// <summary>
|
||||
/// Contains Z as 1 byte.
|
||||
/// </summary>
|
||||
ZInt8 = 8,
|
||||
/// <summary>
|
||||
/// Contains Z as 2 bytes.
|
||||
/// </summary>
|
||||
ZInt16 = 16,
|
||||
/// <summary>
|
||||
/// Contains Z as 4 bytes.
|
||||
/// </summary>
|
||||
ZInt32 = 32,
|
||||
/// <summary>
|
||||
/// Contains Y as 1 byte.
|
||||
/// </summary>
|
||||
YInt8 = 64,
|
||||
/// <summary>
|
||||
/// Contains Y as 2 bytes.
|
||||
/// </summary>
|
||||
YInt32 = 128
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
internal enum DeltaWholeType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates there is no compression. This can also be used to initialize the enum.
|
||||
/// </summary>
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// Data is written as a byte.
|
||||
/// </summary>
|
||||
UInt8 = 1,
|
||||
/// <summary>
|
||||
/// Data is written as a ushort.
|
||||
/// </summary>
|
||||
UInt16 = 2,
|
||||
/// <summary>
|
||||
/// Data is written as a uint.
|
||||
/// </summary>
|
||||
UInt32 = 4,
|
||||
/// <summary>
|
||||
/// Data is written as a ulong.
|
||||
/// </summary>
|
||||
UInt64 = 8,
|
||||
/// <summary>
|
||||
/// data is written as two ulong.
|
||||
/// </summary>
|
||||
UInt128 = 16,
|
||||
/// <summary>
|
||||
/// When set this indicates the new value is larger than the previous.
|
||||
/// When not set, indicates new value is smaller than the previous.
|
||||
/// </summary>
|
||||
NextValueIsLarger = 32
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is for internal use and may change at any time.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
public enum UDeltaPrecisionType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates there is no compression. This can also be used to initialize the enum.
|
||||
/// </summary>
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// Data is written as a byte.
|
||||
/// </summary>
|
||||
UInt8 = 1,
|
||||
/// <summary>
|
||||
/// Data is written as a ushort.
|
||||
/// </summary>
|
||||
UInt16 = 2,
|
||||
/// <summary>
|
||||
/// Data is written as a uint.
|
||||
/// </summary>
|
||||
UInt32 = 4,
|
||||
/// <summary>
|
||||
/// Data is written as a ulong.
|
||||
/// </summary>
|
||||
UInt64 = 8,
|
||||
/// <summary>
|
||||
/// data is written as two ulong.
|
||||
/// </summary>
|
||||
UInt128 = 16,
|
||||
/// <summary>
|
||||
/// When set this indicates the new value is larger than the previous.
|
||||
/// When not set, indicates new value is smaller than the previous.
|
||||
/// </summary>
|
||||
NextValueIsLarger = 128
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is for internal use and may change at any time.
|
||||
/// </summary>
|
||||
public static class DeltaTypeExtensions
|
||||
{
|
||||
public static bool FastContains(this UDeltaPrecisionType whole, UDeltaPrecisionType part) => (whole & part) == part;
|
||||
public static bool FastContains(this UDeltaPrecisionType whole, UDeltaPrecisionType part, int shift) => FastContains((int)whole, (int)part, shift);
|
||||
public static bool FastContains(this DeltaVector3Type whole, DeltaVector3Type part) => (whole & part) == part;
|
||||
public static bool FastContains(this DeltaVector3Type whole, DeltaVector3Type part, int shift) => FastContains((int)whole, (int)part, shift);
|
||||
public static bool FastContains(this DeltaVector2Type whole, DeltaVector2Type part) => (whole & part) == part;
|
||||
|
||||
private static bool FastContains(int whole, int part, int shift)
|
||||
{
|
||||
int intPart = part >> shift;
|
||||
return (whole & intPart) == intPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d9187bce6e160143aa8f742a5f6e0e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/DeltaTypes.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,41 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Utility;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to read generic types.
|
||||
/// </summary>
|
||||
[APIExclude]
|
||||
public static class GenericDeltaReader<T>
|
||||
{
|
||||
public static Func<Reader, T, T> Read { get; internal set; }
|
||||
/// <summary>
|
||||
/// True if this type has a custom writer.
|
||||
/// </summary>
|
||||
internal static bool HasCustomSerializer;
|
||||
|
||||
public static void SetRead(Func<Reader, T, T> value)
|
||||
{
|
||||
/* If a custom serializer has already been set then exit method
|
||||
* to not overwrite serializer. */
|
||||
if (HasCustomSerializer)
|
||||
return;
|
||||
|
||||
bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedReaderPrefix);
|
||||
/* If generated then see if a regular custom writer exists. If so
|
||||
* then do not set a serializer to a generated one. */
|
||||
// TODO Make it so DefaultDeltaReader methods are picked up by codegen.
|
||||
if (isGenerated && GenericReader<T>.HasCustomSerializer)
|
||||
return;
|
||||
|
||||
// Set has custom serializer if value being used is not a generated method.
|
||||
HasCustomSerializer = !isGenerated;
|
||||
Read = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4024d774dc58c64409f498ad4f406ce9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,42 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Utility;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to write generic types.
|
||||
/// </summary>
|
||||
[APIExclude]
|
||||
public static class GenericDeltaWriter<T>
|
||||
{
|
||||
public static Func<Writer, T, T, DeltaSerializerOption, bool> Write { get; internal set; }
|
||||
/// <summary>
|
||||
/// True if this type has a custom writer.
|
||||
/// </summary>
|
||||
internal static bool HasCustomSerializer;
|
||||
|
||||
public static void SetWrite(Func<Writer, T, T, DeltaSerializerOption, bool> value)
|
||||
{
|
||||
/* If a custom serializer has already been set then exit method
|
||||
* to not overwrite serializer. */
|
||||
if (HasCustomSerializer)
|
||||
return;
|
||||
|
||||
bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedWriterPrefix);
|
||||
/* If generated then see if a regular custom writer exists. If so
|
||||
* then do not set a serializer to a generated one. */
|
||||
// TODO Make it so DefaultDeltaWriter methods are picked up by codegen.
|
||||
if (isGenerated && GenericWriter<T>.HasCustomSerializer)
|
||||
return;
|
||||
|
||||
// Set has custom serializer if value being used is not a generated method.
|
||||
HasCustomSerializer = !isGenerated;
|
||||
Write = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f5527790c602a447b437079b9262a90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,42 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Utility;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
|
||||
//Required for internal tests.
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to read generic types.
|
||||
/// </summary>
|
||||
[APIExclude]
|
||||
public static class GenericReader<T>
|
||||
{
|
||||
public static Func<Reader, T> Read { get; set; }
|
||||
/// <summary>
|
||||
/// True if this type has a custom writer.
|
||||
/// </summary>
|
||||
internal static bool HasCustomSerializer;
|
||||
|
||||
public static void SetRead(Func<Reader, T> value)
|
||||
{
|
||||
/* If a custom serializer has already been set then exit method
|
||||
* to not overwrite serializer. */
|
||||
if (HasCustomSerializer)
|
||||
return;
|
||||
|
||||
bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedReaderPrefix);
|
||||
|
||||
//If not generated then unset any generated delta serializer.
|
||||
if (!isGenerated && GenericDeltaReader<T>.HasCustomSerializer)
|
||||
GenericDeltaReader<T>.Read = null;
|
||||
|
||||
//Set has custom serializer if value being used is not a generated method.
|
||||
HasCustomSerializer = !isGenerated;
|
||||
Read = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a02167b94bcdd84da90bbcf5d6ad649
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/GenericReader.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,40 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Utility;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)]
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to write generic types.
|
||||
/// </summary>
|
||||
[APIExclude]
|
||||
public static class GenericWriter<T>
|
||||
{
|
||||
public static Action<Writer, T> Write { get; private set; }
|
||||
/// <summary>
|
||||
/// True if this type has a custom writer.
|
||||
/// </summary>
|
||||
internal static bool HasCustomSerializer;
|
||||
|
||||
public static void SetWrite(Action<Writer, T> value)
|
||||
{
|
||||
/* If a custom serializer has already been set then exit method
|
||||
* to not overwrite serializer. */
|
||||
if (HasCustomSerializer)
|
||||
return;
|
||||
|
||||
bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedWriterPrefix);
|
||||
|
||||
// If not generated then unset any generated delta serializer.
|
||||
if (!isGenerated && GenericDeltaWriter<T>.HasCustomSerializer)
|
||||
GenericDeltaWriter<T>.Write = null;
|
||||
|
||||
// Set has custom serializer if value being used is not a generated method.
|
||||
HasCustomSerializer = !isGenerated;
|
||||
Write = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c79ac203808e16489cbc2072fad75db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/GenericWriter.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47d64f495dba6cf4da8615f022f5a801
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,218 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Serializing;
|
||||
using FishNet.Transporting;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.Broadcast.Helping
|
||||
{
|
||||
internal static class BroadcastsSerializers
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a broadcast to writer.
|
||||
/// </summary>
|
||||
internal static PooledWriter WriteBroadcast<T>(NetworkManager networkManager, PooledWriter writer, T message, ref Channel channel)
|
||||
{
|
||||
writer.WritePacketIdUnpacked(PacketId.Broadcast);
|
||||
writer.WriteUInt16(typeof(T).FullName.GetStableHashU16());
|
||||
// Write data to a new writer.
|
||||
PooledWriter dataWriter = WriterPool.Retrieve();
|
||||
dataWriter.Write(message);
|
||||
// Write length of data.
|
||||
writer.WriteInt32(dataWriter.Length);
|
||||
// Write data.
|
||||
writer.WriteArraySegment(dataWriter.GetArraySegment());
|
||||
// Update channel to reliable if needed.
|
||||
networkManager.TransportManager.CheckSetReliableChannel(writer.Length, ref channel);
|
||||
|
||||
dataWriter.Store();
|
||||
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class BroadcastExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the key for a broadcast type.
|
||||
/// </summary>
|
||||
/// <typeparam name = "T"></typeparam>
|
||||
/// <param name = "broadcastType"></param>
|
||||
/// <returns></returns>
|
||||
internal static ushort GetKey<T>()
|
||||
{
|
||||
return typeof(T).FullName.GetStableHashU16();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implemented by server and client broadcast handlers.
|
||||
/// </summary>
|
||||
public abstract class BroadcastHandlerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Current index when iterating invokes.
|
||||
/// This value will be -1 when not iterating.
|
||||
/// </summary>
|
||||
protected int IteratingIndex;
|
||||
public abstract void RegisterHandler(object obj);
|
||||
public abstract void UnregisterHandler(object obj);
|
||||
public virtual void InvokeHandlers(PooledReader reader, Channel channel) { }
|
||||
public virtual void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) { }
|
||||
public virtual bool RequireAuthentication => false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles broadcasts received on server, from clients.
|
||||
/// </summary>
|
||||
internal class ClientBroadcastHandler<T> : BroadcastHandlerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Action handlers for the broadcast.
|
||||
/// </summary>
|
||||
private List<Action<NetworkConnection, T, Channel>> _handlers = new();
|
||||
/// <summary>
|
||||
/// True to require authentication for the broadcast type.
|
||||
/// </summary>
|
||||
private bool _requireAuthentication;
|
||||
|
||||
public ClientBroadcastHandler(bool requireAuthentication)
|
||||
{
|
||||
_requireAuthentication = requireAuthentication;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes handlers after reading broadcast.
|
||||
/// </summary>
|
||||
/// <returns>True if a rebuild was required.</returns>
|
||||
public override void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel)
|
||||
{
|
||||
T result = reader.Read<T>();
|
||||
for (IteratingIndex = 0; IteratingIndex < _handlers.Count; IteratingIndex++)
|
||||
{
|
||||
Action<NetworkConnection, T, Channel> item = _handlers[IteratingIndex];
|
||||
if (item != null)
|
||||
{
|
||||
item.Invoke(conn, result, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
_handlers.RemoveAt(IteratingIndex);
|
||||
IteratingIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
IteratingIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a handler for this type.
|
||||
/// </summary>
|
||||
public override void RegisterHandler(object obj)
|
||||
{
|
||||
Action<NetworkConnection, T, Channel> handler = (Action<NetworkConnection, T, Channel>)obj;
|
||||
_handlers.AddUnique(handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a handler from this type.
|
||||
/// </summary>
|
||||
/// <param name = "handler"></param>
|
||||
public override void UnregisterHandler(object obj)
|
||||
{
|
||||
Action<NetworkConnection, T, Channel> handler = (Action<NetworkConnection, T, Channel>)obj;
|
||||
int indexOf = _handlers.IndexOf(handler);
|
||||
// Not registered.
|
||||
if (indexOf == -1)
|
||||
return;
|
||||
|
||||
/* Has already been iterated over, need to subtract
|
||||
* 1 from iteratingIndex to accomodate
|
||||
* for the entry about to be removed. */
|
||||
if (IteratingIndex >= 0 && indexOf <= IteratingIndex)
|
||||
IteratingIndex--;
|
||||
|
||||
// Remove entry.
|
||||
_handlers.RemoveAt(indexOf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True to require authentication for the broadcast type.
|
||||
/// </summary>
|
||||
public override bool RequireAuthentication => _requireAuthentication;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles broadcasts received on client, from server.
|
||||
/// </summary>
|
||||
internal class ServerBroadcastHandler<T> : BroadcastHandlerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Action handlers for the broadcast.
|
||||
/// Even though List lookups are slower this allows easy adding and removing of entries during iteration.
|
||||
/// </summary>
|
||||
private List<Action<T, Channel>> _handlers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Invokes handlers after reading broadcast.
|
||||
/// </summary>
|
||||
/// <returns>True if a rebuild was required.</returns>
|
||||
public override void InvokeHandlers(PooledReader reader, Channel channel)
|
||||
{
|
||||
T result = reader.Read<T>();
|
||||
for (IteratingIndex = 0; IteratingIndex < _handlers.Count; IteratingIndex++)
|
||||
{
|
||||
Action<T, Channel> item = _handlers[IteratingIndex];
|
||||
if (item != null)
|
||||
{
|
||||
item.Invoke(result, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
_handlers.RemoveAt(IteratingIndex);
|
||||
IteratingIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
IteratingIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a handler for this type.
|
||||
/// </summary>
|
||||
public override void RegisterHandler(object obj)
|
||||
{
|
||||
Action<T, Channel> handler = (Action<T, Channel>)obj;
|
||||
_handlers.AddUnique(handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a handler from this type.
|
||||
/// </summary>
|
||||
/// <param name = "handler"></param>
|
||||
public override void UnregisterHandler(object obj)
|
||||
{
|
||||
Action<T, Channel> handler = (Action<T, Channel>)obj;
|
||||
int indexOf = _handlers.IndexOf(handler);
|
||||
// Not registered.
|
||||
if (indexOf == -1)
|
||||
return;
|
||||
|
||||
/* Has already been iterated over, need to subtract
|
||||
* 1 from iteratingIndex to accomodate
|
||||
* for the entry about to be removed. */
|
||||
if (IteratingIndex >= 0 && indexOf <= IteratingIndex)
|
||||
IteratingIndex--;
|
||||
|
||||
// Remove entry.
|
||||
_handlers.RemoveAt(indexOf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True to require authentication for the broadcast type.
|
||||
/// </summary>
|
||||
public override bool RequireAuthentication => false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0636e29429649a24795091f80edbd892
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
public class PublicPropertyComparer<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare if T is default.
|
||||
/// </summary>
|
||||
public static Func<T, bool> IsDefault { get; set; }
|
||||
/// <summary>
|
||||
/// Compare if T is the same as T2.
|
||||
/// </summary>
|
||||
public static Func<T, T, bool> Compare { get; set; }
|
||||
}
|
||||
|
||||
public class Comparers
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if A equals B using EqualityCompare.
|
||||
/// </summary>
|
||||
/// <typeparam name = "T"></typeparam>
|
||||
/// <param name = "a"></param>
|
||||
/// <param name = "b"></param>
|
||||
/// <returns></returns>
|
||||
public static bool EqualityCompare<T>(T a, T b)
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(a, b);
|
||||
}
|
||||
|
||||
public static bool IsDefault<T>(T t)
|
||||
{
|
||||
return t.Equals(default(T));
|
||||
}
|
||||
|
||||
public static bool IsEqualityCompareDefault<T>(T a)
|
||||
{
|
||||
return EqualityComparer<T>.Default.Equals(a, default);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SceneComparer : IEqualityComparer<Scene>
|
||||
{
|
||||
public bool Equals(Scene a, Scene b)
|
||||
{
|
||||
if (!a.IsValid() || !b.IsValid())
|
||||
return false;
|
||||
|
||||
if (a.handle != 0 || b.handle != 0)
|
||||
return a.handle == b.handle;
|
||||
|
||||
return a.name == b.name;
|
||||
}
|
||||
|
||||
public int GetHashCode(Scene obj)
|
||||
{
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e912d0645f10b2c458cc2f01e24ecc27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
public static class Quaternion32Compression
|
||||
{
|
||||
private const float Maximum = +1.0f / 1.414214f;
|
||||
private const int BitsPerAxis = 10;
|
||||
private const int LargestComponentShift = BitsPerAxis * 3;
|
||||
private const int AShift = BitsPerAxis * 2;
|
||||
private const int BShift = BitsPerAxis * 1;
|
||||
private const int IntScale = (1 << (BitsPerAxis - 1)) - 1;
|
||||
private const int IntMask = (1 << BitsPerAxis) - 1;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name = "writer"></param>
|
||||
/// <param name = "quaternion"></param>
|
||||
/// <param name = "axesFlippingEnabled">True to flip the smaller values when the largest axes is negative. Doing this saves a byte but the rotation numeric values will be reversed when decompressed.</param>
|
||||
public static void Compress(Writer writer, Quaternion quaternion, bool axesFlippingEnabled = true)
|
||||
{
|
||||
const float precision = 0.00098f;
|
||||
|
||||
float absX = Mathf.Abs(quaternion.x);
|
||||
float absY = Mathf.Abs(quaternion.y);
|
||||
float absZ = Mathf.Abs(quaternion.z);
|
||||
float absW = Mathf.Abs(quaternion.w);
|
||||
|
||||
ComponentType largestComponent = ComponentType.X;
|
||||
float largestAbs = absX;
|
||||
float largest = quaternion.x;
|
||||
|
||||
if (absY > largestAbs)
|
||||
{
|
||||
largestAbs = absY;
|
||||
largestComponent = ComponentType.Y;
|
||||
largest = quaternion.y;
|
||||
}
|
||||
if (absZ > largestAbs)
|
||||
{
|
||||
largestAbs = absZ;
|
||||
largestComponent = ComponentType.Z;
|
||||
largest = quaternion.z;
|
||||
}
|
||||
if (absW > largestAbs)
|
||||
{
|
||||
largestComponent = ComponentType.W;
|
||||
largest = quaternion.w;
|
||||
}
|
||||
|
||||
bool largestIsNegative = largest < 0;
|
||||
|
||||
// If not flipping axes and any values are less than precision then 0 them out.
|
||||
if (!axesFlippingEnabled)
|
||||
{
|
||||
if (absX < precision)
|
||||
quaternion.x = 0f;
|
||||
if (absY < precision)
|
||||
quaternion.y = 0f;
|
||||
if (absZ < precision)
|
||||
quaternion.z = 0f;
|
||||
if (absW < precision)
|
||||
quaternion.w = 0f;
|
||||
}
|
||||
|
||||
float a = 0;
|
||||
float b = 0;
|
||||
float c = 0;
|
||||
switch (largestComponent)
|
||||
{
|
||||
case ComponentType.X:
|
||||
a = quaternion.y;
|
||||
b = quaternion.z;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.Y:
|
||||
a = quaternion.x;
|
||||
b = quaternion.z;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.Z:
|
||||
a = quaternion.x;
|
||||
b = quaternion.y;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.W:
|
||||
a = quaternion.x;
|
||||
b = quaternion.y;
|
||||
c = quaternion.z;
|
||||
break;
|
||||
}
|
||||
|
||||
// If it's okay to flip when largest is negative.
|
||||
if (largestIsNegative && axesFlippingEnabled)
|
||||
{
|
||||
a = -a;
|
||||
b = -b;
|
||||
c = -c;
|
||||
}
|
||||
|
||||
uint integerA = ScaleToUint(a);
|
||||
uint integerB = ScaleToUint(b);
|
||||
uint integerC = ScaleToUint(c);
|
||||
|
||||
if (!axesFlippingEnabled)
|
||||
writer.WriteBoolean(largest < 0f);
|
||||
|
||||
uint result = ((uint)largestComponent << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC;
|
||||
writer.WriteUInt32Unpacked(result);
|
||||
}
|
||||
|
||||
private static uint ScaleToUint(float v)
|
||||
{
|
||||
float normalized = v / Maximum;
|
||||
return (uint)Mathf.RoundToInt(normalized * IntScale) & IntMask;
|
||||
}
|
||||
|
||||
private static float ScaleToFloat(uint v)
|
||||
{
|
||||
float unscaled = v * Maximum / IntScale;
|
||||
|
||||
if (unscaled > Maximum)
|
||||
unscaled -= Maximum * 2;
|
||||
return unscaled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name = "reader"></param>
|
||||
/// <param name = "axesFlippingEnabled">True if the smaller values were flipped during compression when the largest axes was negative.</param>
|
||||
/// <returns></returns>
|
||||
public static Quaternion Decompress(Reader reader, bool axesFlippingEnabled = true)
|
||||
{
|
||||
bool largestIsNegative = axesFlippingEnabled ? false : reader.ReadBoolean();
|
||||
uint compressed = reader.ReadUInt32Unpacked();
|
||||
|
||||
ComponentType largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
|
||||
uint integerA = (compressed >> AShift) & IntMask;
|
||||
uint integerB = (compressed >> BShift) & IntMask;
|
||||
uint integerC = compressed & IntMask;
|
||||
|
||||
float a = ScaleToFloat(integerA);
|
||||
float b = ScaleToFloat(integerB);
|
||||
float c = ScaleToFloat(integerC);
|
||||
|
||||
Quaternion rotation;
|
||||
switch (largestComponentType)
|
||||
{
|
||||
case ComponentType.X:
|
||||
// (?) y z w
|
||||
rotation.y = a;
|
||||
rotation.z = b;
|
||||
rotation.w = c;
|
||||
rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y - rotation.z * rotation.z - rotation.w * rotation.w);
|
||||
|
||||
if (largestIsNegative)
|
||||
rotation.x *= -1f;
|
||||
break;
|
||||
case ComponentType.Y:
|
||||
// x (?) z w
|
||||
rotation.x = a;
|
||||
rotation.z = b;
|
||||
rotation.w = c;
|
||||
rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.z * rotation.z - rotation.w * rotation.w);
|
||||
|
||||
if (largestIsNegative)
|
||||
rotation.y *= -1f;
|
||||
break;
|
||||
case ComponentType.Z:
|
||||
// x y (?) w
|
||||
rotation.x = a;
|
||||
rotation.y = b;
|
||||
rotation.w = c;
|
||||
rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.y * rotation.y - rotation.w * rotation.w);
|
||||
|
||||
if (largestIsNegative)
|
||||
rotation.z *= -1f;
|
||||
break;
|
||||
case ComponentType.W:
|
||||
// x y z (?)
|
||||
rotation.x = a;
|
||||
rotation.y = b;
|
||||
rotation.z = c;
|
||||
rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.y * rotation.y - rotation.z * rotation.z);
|
||||
|
||||
if (largestIsNegative)
|
||||
rotation.w *= -1f;
|
||||
break;
|
||||
default:
|
||||
// Should never happen!
|
||||
throw new ArgumentOutOfRangeException("Unknown rotation component type: " + largestComponentType);
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f71e61ed84064a0429577ec462a8fa79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/Quaternion32Compression.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
/// <summary>
|
||||
/// Credit to https://github.com/viliwonka
|
||||
/// https://github.com/FirstGearGames/FishNet/pull/23
|
||||
/// </summary>
|
||||
public static class Quaternion64Compression
|
||||
{
|
||||
// 64 bit quaternion compression
|
||||
// [4 bits] largest component
|
||||
// [21 bits] higher res
|
||||
// [21 bits] higher res
|
||||
// [20 bits] higher res
|
||||
// sum is 64 bits
|
||||
private const float Maximum = +1.0f / 1.414214f;
|
||||
private const int BitsPerAxis_H = 21; // higher res, 21 bits
|
||||
private const int BitsPerAxis_L = 20; // lower res, 20 bits
|
||||
private const int LargestComponentShift = BitsPerAxis_H * 2 + BitsPerAxis_L * 1;
|
||||
private const int AShift = BitsPerAxis_H + BitsPerAxis_L;
|
||||
private const int BShift = BitsPerAxis_L;
|
||||
private const int IntScale_H = (1 << (BitsPerAxis_H - 1)) - 1;
|
||||
private const int IntMask_H = (1 << BitsPerAxis_H) - 1;
|
||||
private const int IntScale_L = (1 << (BitsPerAxis_L - 1)) - 1;
|
||||
private const int IntMask_L = (1 << BitsPerAxis_L) - 1;
|
||||
|
||||
public static ulong Compress(Quaternion quaternion)
|
||||
{
|
||||
float absX = Mathf.Abs(quaternion.x);
|
||||
float absY = Mathf.Abs(quaternion.y);
|
||||
float absZ = Mathf.Abs(quaternion.z);
|
||||
float absW = Mathf.Abs(quaternion.w);
|
||||
|
||||
ComponentType largestComponent = ComponentType.X;
|
||||
float largestAbs = absX;
|
||||
float largest = quaternion.x;
|
||||
|
||||
if (absY > largestAbs)
|
||||
{
|
||||
largestAbs = absY;
|
||||
largestComponent = ComponentType.Y;
|
||||
largest = quaternion.y;
|
||||
}
|
||||
if (absZ > largestAbs)
|
||||
{
|
||||
largestAbs = absZ;
|
||||
largestComponent = ComponentType.Z;
|
||||
largest = quaternion.z;
|
||||
}
|
||||
if (absW > largestAbs)
|
||||
{
|
||||
largestComponent = ComponentType.W;
|
||||
largest = quaternion.w;
|
||||
}
|
||||
|
||||
float a = 0;
|
||||
float b = 0;
|
||||
float c = 0;
|
||||
|
||||
switch (largestComponent)
|
||||
{
|
||||
case ComponentType.X:
|
||||
a = quaternion.y;
|
||||
b = quaternion.z;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.Y:
|
||||
a = quaternion.x;
|
||||
b = quaternion.z;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.Z:
|
||||
a = quaternion.x;
|
||||
b = quaternion.y;
|
||||
c = quaternion.w;
|
||||
break;
|
||||
case ComponentType.W:
|
||||
a = quaternion.x;
|
||||
b = quaternion.y;
|
||||
c = quaternion.z;
|
||||
break;
|
||||
}
|
||||
|
||||
if (largest < 0)
|
||||
{
|
||||
a = -a;
|
||||
b = -b;
|
||||
c = -c;
|
||||
}
|
||||
|
||||
ulong integerA = ScaleToUint_H(a);
|
||||
ulong integerB = ScaleToUint_H(b);
|
||||
ulong integerC = ScaleToUint_L(c);
|
||||
|
||||
return ((ulong)largestComponent << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC;
|
||||
}
|
||||
|
||||
private static ulong ScaleToUint_H(float v)
|
||||
{
|
||||
float normalized = v / Maximum;
|
||||
return (ulong)Mathf.RoundToInt(normalized * IntScale_H) & IntMask_H;
|
||||
}
|
||||
|
||||
private static ulong ScaleToUint_L(float v)
|
||||
{
|
||||
float normalized = v / Maximum;
|
||||
return (ulong)Mathf.RoundToInt(normalized * IntScale_L) & IntMask_L;
|
||||
}
|
||||
|
||||
private static float ScaleToFloat_H(ulong v)
|
||||
{
|
||||
float unscaled = v * Maximum / IntScale_H;
|
||||
|
||||
if (unscaled > Maximum)
|
||||
unscaled -= Maximum * 2;
|
||||
return unscaled;
|
||||
}
|
||||
|
||||
private static float ScaleToFloat_L(ulong v)
|
||||
{
|
||||
float unscaled = v * Maximum / IntScale_L;
|
||||
|
||||
if (unscaled > Maximum)
|
||||
unscaled -= Maximum * 2;
|
||||
return unscaled;
|
||||
}
|
||||
|
||||
public static Quaternion Decompress(ulong compressed)
|
||||
{
|
||||
ComponentType largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
|
||||
ulong integerA = (compressed >> AShift) & IntMask_H;
|
||||
ulong integerB = (compressed >> BShift) & IntMask_H;
|
||||
ulong integerC = compressed & IntMask_L;
|
||||
|
||||
float a = ScaleToFloat_H(integerA);
|
||||
float b = ScaleToFloat_H(integerB);
|
||||
float c = ScaleToFloat_L(integerC);
|
||||
|
||||
Quaternion rotation;
|
||||
switch (largestComponentType)
|
||||
{
|
||||
case ComponentType.X:
|
||||
// (?) y z w
|
||||
rotation.y = a;
|
||||
rotation.z = b;
|
||||
rotation.w = c;
|
||||
rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y - rotation.z * rotation.z - rotation.w * rotation.w);
|
||||
break;
|
||||
case ComponentType.Y:
|
||||
// x (?) z w
|
||||
rotation.x = a;
|
||||
rotation.z = b;
|
||||
rotation.w = c;
|
||||
rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.z * rotation.z - rotation.w * rotation.w);
|
||||
break;
|
||||
case ComponentType.Z:
|
||||
// x y (?) w
|
||||
rotation.x = a;
|
||||
rotation.y = b;
|
||||
rotation.w = c;
|
||||
rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.y * rotation.y - rotation.w * rotation.w);
|
||||
break;
|
||||
case ComponentType.W:
|
||||
// x y z (?)
|
||||
rotation.x = a;
|
||||
rotation.y = b;
|
||||
rotation.z = c;
|
||||
rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x - rotation.y * rotation.y - rotation.z * rotation.z);
|
||||
break;
|
||||
default:
|
||||
// Should never happen!
|
||||
throw new ArgumentOutOfRangeException("Unknown rotation component type: " + largestComponentType);
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7afd33d2ca5433f4f831dfaf0169423c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/Quaternion64Compression.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
public struct QuaternionAutoPack
|
||||
{
|
||||
public Quaternion Value;
|
||||
public AutoPackType PackType;
|
||||
|
||||
public QuaternionAutoPack(Quaternion value)
|
||||
{
|
||||
Value = value;
|
||||
PackType = AutoPackType.Packed;
|
||||
}
|
||||
|
||||
public QuaternionAutoPack(Quaternion value, AutoPackType autoPackType)
|
||||
{
|
||||
Value = value;
|
||||
PackType = autoPackType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class QuaternionAutoPackExtensions
|
||||
{
|
||||
public static void WriteQuaternionAutoPack(this Writer w, QuaternionAutoPack value)
|
||||
{
|
||||
w.WriteUInt8Unpacked((byte)value.PackType);
|
||||
w.WriteQuaternion(value.Value, value.PackType);
|
||||
}
|
||||
|
||||
public static QuaternionAutoPack ReadUnpackedQuaternion(this Reader reader)
|
||||
{
|
||||
AutoPackType packType = (AutoPackType)reader.ReadUInt8Unpacked();
|
||||
Quaternion q = reader.ReadQuaternion(packType);
|
||||
|
||||
return new(q, packType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae3125324bcaa3347ab27b1ebcd1270e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionAutoPack.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
public enum ComponentType : uint
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2,
|
||||
W = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7ac59ce12259104fa28fc837fb17ccf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,278 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FishNet.Managing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
[Flags]
|
||||
internal enum QuaternionDeltaPrecisionFlag : byte
|
||||
{
|
||||
Unset = 0,
|
||||
/* Its probably safe to discard '-IsNegative'
|
||||
* and replace with a single 'largest is negative'.
|
||||
* Doing this would still use the same amount of bytes
|
||||
* though, and would require a refactor on this and the delta
|
||||
* compression class. */
|
||||
NextAIsLarger = 1 << 0,
|
||||
NextBIsLarger = 1 << 1,
|
||||
NextCIsLarger = 1 << 2,
|
||||
NextDIsNegative = 1 << 3,
|
||||
LargestIsX = 1 << 4,
|
||||
LargestIsY = 1 << 5,
|
||||
LargestIsZ = 1 << 6,
|
||||
// This flag can be discarded via refactor if we need it later.
|
||||
LargestIsW = 1 << 7
|
||||
}
|
||||
|
||||
internal static class QuaternionDeltaPrecisionFlagExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if whole contains part.
|
||||
/// </summary>
|
||||
internal static bool FastContains(this QuaternionDeltaPrecisionFlag whole, QuaternionDeltaPrecisionFlag part) => (whole & part) == part;
|
||||
}
|
||||
|
||||
public static class QuaternionDeltaPrecisionCompression
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a compressed a delta Quaternion using a variable precision.
|
||||
/// </summary>
|
||||
public static void Compress(Writer writer, Quaternion valueA, Quaternion valueB, float precision = 0.001f)
|
||||
{
|
||||
uint multiplier = (uint)Mathf.RoundToInt(1f / precision);
|
||||
|
||||
// Position where the next byte is to be written.
|
||||
int startPosition = writer.Position;
|
||||
// Skip one byte so the flags can be inserted after everything else is writteh.
|
||||
writer.Skip(1);
|
||||
|
||||
QuaternionDeltaPrecisionFlag flags = QuaternionDeltaPrecisionFlag.Unset;
|
||||
long largestUValue = -1;
|
||||
|
||||
/* This becomes true if the largest difference is negative on valueB.
|
||||
* EG: if Y is the largest and value.Y is < 0f then largestIsNegative becomes true. */
|
||||
bool largestIsNegative = false;
|
||||
|
||||
/* Set next is larger values, and output differneces. */
|
||||
bool xIsLarger = GetNextIsLarger(valueA.x, valueB.x, multiplier, out uint xDifference);
|
||||
UpdateLargestValues(xDifference, valueB.x, QuaternionDeltaPrecisionFlag.LargestIsX);
|
||||
|
||||
bool yIsLarger = GetNextIsLarger(valueA.y, valueB.y, multiplier, out uint yDifference);
|
||||
UpdateLargestValues(yDifference, valueB.y, QuaternionDeltaPrecisionFlag.LargestIsY);
|
||||
|
||||
bool zIsLarger = GetNextIsLarger(valueA.z, valueB.z, multiplier, out uint zDifference);
|
||||
UpdateLargestValues(zDifference, valueB.z, QuaternionDeltaPrecisionFlag.LargestIsZ);
|
||||
|
||||
bool wIsLarger = GetNextIsLarger(valueA.w, valueB.w, multiplier, out uint wDifference);
|
||||
UpdateLargestValues(wDifference, valueB.w, QuaternionDeltaPrecisionFlag.LargestIsW);
|
||||
|
||||
// If flags are unset something went wrong. This should never be possible.
|
||||
if (flags == QuaternionDeltaPrecisionFlag.Unset)
|
||||
{
|
||||
// Write that flags are unset and error.
|
||||
writer.InsertUInt8Unpacked((byte)flags, startPosition);
|
||||
writer.NetworkManager.LogError($"Flags should not be unset.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates largest values and flags.
|
||||
void UpdateLargestValues(uint checkedValue, float fValue, QuaternionDeltaPrecisionFlag newFlag)
|
||||
{
|
||||
if (checkedValue > largestUValue)
|
||||
{
|
||||
largestUValue = checkedValue;
|
||||
flags = newFlag;
|
||||
largestIsNegative = fValue < 0f;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all but largest. */
|
||||
|
||||
// X is largest.
|
||||
if (flags == QuaternionDeltaPrecisionFlag.LargestIsX)
|
||||
WriteValues(yDifference, yIsLarger, zDifference, zIsLarger, wDifference, wIsLarger);
|
||||
// Y is largest.
|
||||
else if (flags == QuaternionDeltaPrecisionFlag.LargestIsY)
|
||||
WriteValues(xDifference, xIsLarger, zDifference, zIsLarger, wDifference, wIsLarger);
|
||||
// Z is largest.
|
||||
else if (flags == QuaternionDeltaPrecisionFlag.LargestIsZ)
|
||||
WriteValues(xDifference, xIsLarger, yDifference, yIsLarger, wDifference, wIsLarger);
|
||||
// W is largest.
|
||||
else if (flags == QuaternionDeltaPrecisionFlag.LargestIsW)
|
||||
WriteValues(xDifference, xIsLarger, yDifference, yIsLarger, zDifference, zIsLarger);
|
||||
|
||||
/* This must be set after values are written since the enum
|
||||
* checks above use ==, rather than a bit comparer. */
|
||||
if (largestIsNegative)
|
||||
flags |= QuaternionDeltaPrecisionFlag.NextDIsNegative;
|
||||
|
||||
void WriteValues(uint aValue, bool aIsLarger, uint bValue, bool bIsLarger, uint cValue, bool cIsLarger)
|
||||
{
|
||||
writer.WriteUnsignedPackedWhole(aValue);
|
||||
if (aIsLarger)
|
||||
flags |= QuaternionDeltaPrecisionFlag.NextAIsLarger;
|
||||
|
||||
writer.WriteUnsignedPackedWhole(bValue);
|
||||
if (bIsLarger)
|
||||
flags |= QuaternionDeltaPrecisionFlag.NextBIsLarger;
|
||||
|
||||
writer.WriteUnsignedPackedWhole(cValue);
|
||||
if (cIsLarger)
|
||||
flags |= QuaternionDeltaPrecisionFlag.NextCIsLarger;
|
||||
}
|
||||
|
||||
// Insert flags.
|
||||
writer.InsertUInt8Unpacked((byte)flags, startPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a compressed a delta Quaternion using a variable precision.
|
||||
/// </summary>
|
||||
public static Quaternion Decompress(Reader reader, Quaternion valueA, float precision = 0.001f)
|
||||
{
|
||||
uint multiplier = (uint)Mathf.RoundToInt(1f / precision);
|
||||
|
||||
QuaternionDeltaPrecisionFlag flags = (QuaternionDeltaPrecisionFlag)reader.ReadUInt8Unpacked();
|
||||
|
||||
// Unset flags mean something went wrong in writing.
|
||||
if (flags == QuaternionDeltaPrecisionFlag.Unset)
|
||||
{
|
||||
reader.NetworkManager.LogError($"Unset flags were returned.");
|
||||
return default;
|
||||
}
|
||||
|
||||
/* These values will be in order of X Y Z W.
|
||||
* Whichever value is the highest will be left out.
|
||||
*
|
||||
* EG: if Y was the highest then the following will be true...
|
||||
* a = X
|
||||
* b = Z
|
||||
* c = W */
|
||||
uint aWholeDifference = (uint)reader.ReadUnsignedPackedWhole();
|
||||
uint bWholeDifference = (uint)reader.ReadUnsignedPackedWhole();
|
||||
uint cWholeDifference = (uint)reader.ReadUnsignedPackedWhole();
|
||||
|
||||
// Debug.Log($"Read {aWholeDifference}, {bWholeDifference}, {cWholeDifference}. ValueA {valueA}");
|
||||
|
||||
float aFloatDifference = (float)aWholeDifference / multiplier;
|
||||
float bFloatDifference = (float)bWholeDifference / multiplier;
|
||||
float cFloatDifference = (float)cWholeDifference / multiplier;
|
||||
|
||||
// Invert differences as needed so they can all be added onto the previous value as negative or positive.
|
||||
if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextAIsLarger))
|
||||
aFloatDifference *= -1f;
|
||||
if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextBIsLarger))
|
||||
bFloatDifference *= -1f;
|
||||
if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextCIsLarger))
|
||||
cFloatDifference *= -1f;
|
||||
|
||||
float nextA;
|
||||
float nextB;
|
||||
float nextC;
|
||||
|
||||
/* Add onto the previous value. */
|
||||
if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsX))
|
||||
{
|
||||
nextA = valueA.y + aFloatDifference;
|
||||
nextB = valueA.z + bFloatDifference;
|
||||
nextC = valueA.w + cFloatDifference;
|
||||
}
|
||||
else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsY))
|
||||
{
|
||||
nextA = valueA.x + aFloatDifference;
|
||||
nextB = valueA.z + bFloatDifference;
|
||||
nextC = valueA.w + cFloatDifference;
|
||||
}
|
||||
else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsZ))
|
||||
{
|
||||
nextA = valueA.x + aFloatDifference;
|
||||
nextB = valueA.y + bFloatDifference;
|
||||
nextC = valueA.w + cFloatDifference;
|
||||
}
|
||||
/* We do not really need the 'largest is W' since we know if
|
||||
* the other 3 are not the largest, then the remaining must be.
|
||||
* We have the available packing to use though, so use them
|
||||
* for now. */
|
||||
else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsW))
|
||||
{
|
||||
nextA = valueA.x + aFloatDifference;
|
||||
nextB = valueA.y + bFloatDifference;
|
||||
nextC = valueA.z + cFloatDifference;
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.NetworkManager.LogError($"Largest axes was not handled. Flags {flags}.");
|
||||
return default;
|
||||
}
|
||||
|
||||
float abcMagnitude = GetMagnitude(nextA, nextB, nextC);
|
||||
|
||||
float nextD = 1f - abcMagnitude;
|
||||
/* NextD should always be positive. But depending on precision
|
||||
* the calculated result could be negative due to missing decimals.
|
||||
* When negative make positive so nextD will normalize properly. */
|
||||
if (nextD < 0f)
|
||||
nextD *= -1f;
|
||||
|
||||
nextD = (float)Math.Sqrt(nextD);
|
||||
|
||||
// Get magnitude of all values.
|
||||
static float GetMagnitude(float a, float b, float c, float d = 0f) => a * a + b * b + c * c + d * d;
|
||||
|
||||
if (nextD >= 0f && flags.FastContains(QuaternionDeltaPrecisionFlag.NextDIsNegative))
|
||||
nextD *= -1f;
|
||||
|
||||
if (!TryNormalize())
|
||||
return default;
|
||||
|
||||
// Normalizes next values.
|
||||
bool TryNormalize()
|
||||
{
|
||||
float magnitude = (float)Math.Sqrt(GetMagnitude(nextA, nextB, nextC, nextD));
|
||||
if (magnitude < float.Epsilon)
|
||||
{
|
||||
reader.NetworkManager.LogError($"Magnitude cannot be normalized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
nextA /= magnitude;
|
||||
nextB /= magnitude;
|
||||
nextC /= magnitude;
|
||||
nextD /= magnitude;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Add onto the previous value. */
|
||||
if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsX))
|
||||
return new(nextD, nextA, nextB, nextC);
|
||||
if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsY))
|
||||
return new(nextA, nextD, nextB, nextC);
|
||||
if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsZ))
|
||||
return new(nextA, nextB, nextD, nextC);
|
||||
if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsW))
|
||||
return new(nextA, nextB, nextC, nextD);
|
||||
|
||||
reader.NetworkManager.LogError($"Unhandled Largest flag. Received flags are {flags}.");
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the next value is larger than the previous, and returns unsigned result with multiplier applied.
|
||||
/// </summary>
|
||||
private static bool GetNextIsLarger(float a, float b, uint lMultiplier, out uint multipliedUResult)
|
||||
{
|
||||
// Set is b is larger.
|
||||
bool bIsLarger = b > a;
|
||||
|
||||
// Get multiplied u value.
|
||||
float value = bIsLarger ? b - a : a - b;
|
||||
multipliedUResult = (uint)Mathf.RoundToInt(value * lMultiplier);
|
||||
|
||||
return bIsLarger;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37e43ec9c17f2dc43ac255695bd1a71f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using FishNet.Managing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
[Flags]
|
||||
internal enum QuaternionPrecisionFlag : byte
|
||||
{
|
||||
Unset = 0,
|
||||
/* Its probably safe to discard '-IsNegative'
|
||||
* and replace with a single 'largest is negative'.
|
||||
* Doing this would still use the same amount of bytes
|
||||
* though, and would require a refactor on this and the delta
|
||||
* compression class. */
|
||||
AIsNegative = 1 << 0,
|
||||
BIsNegative = 1 << 1,
|
||||
CIsNegative = 1 << 2,
|
||||
DIsNegative = 1 << 3,
|
||||
LargestIsX = 1 << 4,
|
||||
LargestIsY = 1 << 5,
|
||||
LargestIsZ = 1 << 6,
|
||||
// This flag can be discarded via refactor if we need it later.
|
||||
LargestIsW = 1 << 7
|
||||
}
|
||||
|
||||
internal static class QuaternionPrecisionFlagExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if whole contains part.
|
||||
/// </summary>
|
||||
internal static bool FastContains(this QuaternionPrecisionFlag whole, QuaternionPrecisionFlag part) => (whole & part) == part;
|
||||
}
|
||||
|
||||
public static class QuaternionPrecisionCompression
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a compressed a delta Quaternion using a variable precision.
|
||||
/// </summary>
|
||||
public static void Compress(Writer writer, Quaternion value, float precision = 0.001f)
|
||||
{
|
||||
/* When using 0.001f or less accurate precision use the classic
|
||||
* compression. This saves about a byte by send. */
|
||||
if (precision >= 0.001f)
|
||||
{
|
||||
Quaternion32Compression.Compress(writer, value, axesFlippingEnabled: false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Position where the next byte is to be written.
|
||||
int startPosition = writer.Position;
|
||||
|
||||
// Skip one byte so the flags can be inserted after everything else is writteh.
|
||||
writer.Skip(1);
|
||||
|
||||
QuaternionPrecisionFlag flags = QuaternionPrecisionFlag.Unset;
|
||||
float largestAxesValue = float.MinValue;
|
||||
|
||||
// Find out which value is the largest.
|
||||
UpdateLargestValues(Math.Abs(value.x), QuaternionPrecisionFlag.LargestIsX);
|
||||
UpdateLargestValues(Math.Abs(value.y), QuaternionPrecisionFlag.LargestIsY);
|
||||
UpdateLargestValues(Math.Abs(value.z), QuaternionPrecisionFlag.LargestIsZ);
|
||||
UpdateLargestValues(Math.Abs(value.w), QuaternionPrecisionFlag.LargestIsW);
|
||||
|
||||
// Updates largest values and flags.
|
||||
void UpdateLargestValues(float checkedValue, QuaternionPrecisionFlag newFlag)
|
||||
{
|
||||
if (checkedValue > largestAxesValue)
|
||||
{
|
||||
largestAxesValue = checkedValue;
|
||||
flags = newFlag;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write all but largest. */
|
||||
|
||||
// X is largest.
|
||||
if (flags == QuaternionPrecisionFlag.LargestIsX)
|
||||
WriteValuesAndSetPositives(value.y, value.z, value.w, value.x);
|
||||
// Y is largest.
|
||||
else if (flags == QuaternionPrecisionFlag.LargestIsY)
|
||||
WriteValuesAndSetPositives(value.x, value.z, value.w, value.y);
|
||||
// Z is largest.
|
||||
else if (flags == QuaternionPrecisionFlag.LargestIsZ)
|
||||
WriteValuesAndSetPositives(value.x, value.y, value.w, value.z);
|
||||
// W is largest.
|
||||
else if (flags == QuaternionPrecisionFlag.LargestIsW)
|
||||
WriteValuesAndSetPositives(value.x, value.y, value.z, value.w);
|
||||
|
||||
void WriteValuesAndSetPositives(float aValue, float bValue, float cValue, float largestAxes)
|
||||
{
|
||||
uint multiplier = (uint)Mathf.RoundToInt(1f / precision);
|
||||
|
||||
uint aUint = (uint)Mathf.RoundToInt(Math.Abs(aValue) * multiplier);
|
||||
uint bUint = (uint)Mathf.RoundToInt(Math.Abs(bValue) * multiplier);
|
||||
uint cUint = (uint)Mathf.RoundToInt(Math.Abs(cValue) * multiplier);
|
||||
|
||||
writer.WriteUnsignedPackedWhole(aUint);
|
||||
writer.WriteUnsignedPackedWhole(bUint);
|
||||
writer.WriteUnsignedPackedWhole(cUint);
|
||||
|
||||
/* Update sign on values. */
|
||||
if (aValue < 0f)
|
||||
flags |= QuaternionPrecisionFlag.AIsNegative;
|
||||
if (bValue < 0f)
|
||||
flags |= QuaternionPrecisionFlag.BIsNegative;
|
||||
if (cValue <= 0f)
|
||||
flags |= QuaternionPrecisionFlag.CIsNegative;
|
||||
if (largestAxes < 0f)
|
||||
flags |= QuaternionPrecisionFlag.DIsNegative;
|
||||
}
|
||||
|
||||
// Insert flags.
|
||||
writer.InsertUInt8Unpacked((byte)flags, startPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a compressed a delta Quaternion using a variable precision.
|
||||
/// </summary>
|
||||
public static Quaternion Decompress(Reader reader, float precision = 0.001f)
|
||||
{
|
||||
/* When using 0.001f or less accurate precision use the classic
|
||||
* compression. This saves about a byte by send. */
|
||||
if (precision >= 0.001f)
|
||||
return Quaternion32Compression.Decompress(reader, axesFlippingEnabled: false);
|
||||
|
||||
uint multiplier = (uint)Mathf.RoundToInt(1f / precision);
|
||||
|
||||
QuaternionPrecisionFlag flags = (QuaternionPrecisionFlag)reader.ReadUInt8Unpacked();
|
||||
|
||||
// Unset flags mean something went wrong in writing.
|
||||
if (flags == QuaternionPrecisionFlag.Unset)
|
||||
{
|
||||
reader.NetworkManager.LogError($"Unset flags were returned.");
|
||||
return default;
|
||||
}
|
||||
|
||||
/* These values will be in order of X Y Z W.
|
||||
* Whichever value is the highest will be left out.
|
||||
*
|
||||
* EG: if Y was the highest then the following will be true...
|
||||
* a = X
|
||||
* b = Z
|
||||
* c = W */
|
||||
float aValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier;
|
||||
float bValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier;
|
||||
float cValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier;
|
||||
|
||||
// Make values negative if needed.
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.AIsNegative))
|
||||
aValue *= -1f;
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.BIsNegative))
|
||||
bValue *= -1f;
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.CIsNegative))
|
||||
cValue *= -1f;
|
||||
|
||||
float abcMagnitude = GetMagnitude(aValue, bValue, cValue);
|
||||
|
||||
float dValue = 1f - abcMagnitude;
|
||||
/* NextD should always be positive. But depending on precision
|
||||
* the calculated result could be negative due to missing decimals.
|
||||
* When negative make positive so dValue will normalize properly. */
|
||||
if (dValue < 0f)
|
||||
dValue *= -1f;
|
||||
|
||||
dValue = (float)Math.Sqrt(dValue);
|
||||
|
||||
// Get magnitude of all values.
|
||||
static float GetMagnitude(float a, float b, float c, float d = 0f) => a * a + b * b + c * c + d * d;
|
||||
|
||||
if (dValue >= 0f && flags.FastContains(QuaternionPrecisionFlag.DIsNegative))
|
||||
dValue *= -1f;
|
||||
|
||||
if (!TryNormalize())
|
||||
return default;
|
||||
|
||||
// Normalizes next values.
|
||||
bool TryNormalize()
|
||||
{
|
||||
float magnitude = (float)Math.Sqrt(GetMagnitude(aValue, bValue, cValue, dValue));
|
||||
if (magnitude < float.Epsilon)
|
||||
{
|
||||
reader.NetworkManager.LogError($"Magnitude cannot be normalized.");
|
||||
return false;
|
||||
}
|
||||
|
||||
aValue /= magnitude;
|
||||
bValue /= magnitude;
|
||||
cValue /= magnitude;
|
||||
dValue /= magnitude;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Add onto the previous value. */
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.LargestIsX))
|
||||
return new(dValue, aValue, bValue, cValue);
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.LargestIsY))
|
||||
return new(aValue, dValue, bValue, cValue);
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.LargestIsZ))
|
||||
return new(aValue, bValue, dValue, cValue);
|
||||
if (flags.FastContains(QuaternionPrecisionFlag.LargestIsW))
|
||||
return new(aValue, bValue, cValue, dValue);
|
||||
|
||||
reader.NetworkManager.LogError($"Unhandled Largest flag. Received flags are {flags}.");
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3df94f7449d53c4b8b5a167b51a93dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,159 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using FishNet.Managing;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to reserve bytes in a writer for length, then inserts length after data has been written.
|
||||
/// Reserved values are always written as unsigned.
|
||||
/// </summary>
|
||||
internal class ReservedLengthWriter : IResettable
|
||||
{
|
||||
private Writer _writer;
|
||||
private int _startPosition;
|
||||
private byte _reservedBytes;
|
||||
/// <summary>
|
||||
/// Number of bytes currently written.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get { return _writer == null ? 0 : _writer.Position - _startPosition; }
|
||||
}
|
||||
|
||||
public void Initialize(Writer writer, byte reservedBytes)
|
||||
{
|
||||
_writer = writer;
|
||||
_reservedBytes = reservedBytes;
|
||||
writer.Skip(reservedBytes);
|
||||
_startPosition = writer.Position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the amount of data written to the reserved space.
|
||||
/// This also resets the state of this object.
|
||||
/// </summary>
|
||||
public void WriteLength()
|
||||
{
|
||||
WriteLength((uint)Length);
|
||||
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the amount of data written to the reserved space. If no data was written the reserved amount is removed.
|
||||
/// This also resets the state of this object.
|
||||
/// Returns if length was written.
|
||||
/// </summary>
|
||||
public bool WriteLengthOrRemove(uint written)
|
||||
{
|
||||
if (written == 0)
|
||||
_writer.Remove(_reservedBytes);
|
||||
else
|
||||
WriteLength(written);
|
||||
|
||||
ResetState();
|
||||
|
||||
return written > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the amount of data written to the reserved space. This overrides Length normally written.
|
||||
/// This also resets the state of this object.
|
||||
/// </summary>
|
||||
public void WriteLength(uint written)
|
||||
{
|
||||
switch (_reservedBytes)
|
||||
{
|
||||
case 1:
|
||||
_writer.InsertUInt8Unpacked((byte)written, _startPosition - _reservedBytes);
|
||||
break;
|
||||
case 2:
|
||||
_writer.InsertUInt16Unpacked((ushort)written, _startPosition - _reservedBytes);
|
||||
break;
|
||||
case 4:
|
||||
_writer.InsertUInt32Unpacked((uint)written, _startPosition - _reservedBytes);
|
||||
break;
|
||||
default:
|
||||
NetworkManager nm = _writer == null ? null : _writer.NetworkManager;
|
||||
nm.LogError($"Reserved bytes value of {_reservedBytes} is unhandled.");
|
||||
break;
|
||||
}
|
||||
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the amount of data written to the reserved space. If no data was written the reserved amount is removed.
|
||||
/// This also resets the state of this object.
|
||||
/// </summary>
|
||||
public bool WriteLengthOrRemove()
|
||||
{
|
||||
// Insert written amount.
|
||||
int written = _writer.Position - _startPosition;
|
||||
|
||||
if (written == 0)
|
||||
_writer.Remove(_reservedBytes);
|
||||
else
|
||||
WriteLength((uint)written);
|
||||
|
||||
ResetState();
|
||||
|
||||
return written > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a length read based on a reserved byte count.
|
||||
/// </summary>
|
||||
/// <param name = "resetPosition">True to reset to position before read.</param>
|
||||
public static uint ReadLength(PooledReader reader, byte reservedBytes, bool resetPosition = false)
|
||||
{
|
||||
uint result;
|
||||
switch (reservedBytes)
|
||||
{
|
||||
case 1:
|
||||
result = reader.ReadUInt8Unpacked();
|
||||
break;
|
||||
case 2:
|
||||
result = reader.ReadUInt16Unpacked();
|
||||
break;
|
||||
case 4:
|
||||
result = reader.ReadUInt32Unpacked();
|
||||
break;
|
||||
default:
|
||||
NetworkManager nm = reader == null ? null : reader.NetworkManager;
|
||||
nm.LogError($"Reserved bytes value of {reservedBytes} is unhandled.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (resetPosition)
|
||||
reader.Position -= (int)result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
_writer = null;
|
||||
_startPosition = 0;
|
||||
_reservedBytes = 0;
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
|
||||
internal static class ReservedWritersExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores to a cache.
|
||||
/// </summary>
|
||||
public static void Store(this ReservedLengthWriter rlw) => ResettableObjectCaches<ReservedLengthWriter>.Store(rlw);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves from a cache.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ReservedLengthWriter Retrieve() => ResettableObjectCaches<ReservedLengthWriter>.Retrieve();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9fd1f1770c15f54da31f3903a8714ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct UIntFloat
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public float FloatValue;
|
||||
[FieldOffset(0)]
|
||||
public uint UIntValue;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct UIntDouble
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public double DoubleValue;
|
||||
[FieldOffset(0)]
|
||||
public ulong LongValue;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct UIntDecimal
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public ulong LongValue1;
|
||||
[FieldOffset(8)]
|
||||
public ulong LongValue2;
|
||||
[FieldOffset(0)]
|
||||
public decimal DecimalValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 008e79d0f22a2674189acc7eff64408f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,468 @@
|
||||
using System;
|
||||
using FishNet.CodeGenerating;
|
||||
using System.Runtime.CompilerServices;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Serializing.Helping;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Reader
|
||||
{
|
||||
internal double DOUBLE_ACCURACY => Writer.DOUBLE_ACCURACY;
|
||||
internal decimal DECIMAL_ACCURACY => Writer.DECIMAL_ACCURACY;
|
||||
|
||||
#region Other.
|
||||
/// <summary>
|
||||
/// Reads a boolean.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public bool ReadDeltaBoolean(bool valueA)
|
||||
{
|
||||
return !valueA;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Whole values.
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public sbyte ReadDeltaInt8(sbyte valueA) => (sbyte)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public byte ReadDeltaUInt8(byte valueA) => (byte)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public short ReadDeltaInt16(short valueA) => (short)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public ushort ReadDeltaUInt16(ushort valueA) => (ushort)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public int ReadDeltaInt32(int valueA) => (int)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public uint ReadDeltaUInt32(uint valueA) => (uint)ReadDifference8_16_32(valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public long ReadDeltaInt64(long valueA) => (long)ReadDeltaUInt64((ulong)valueA);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public ulong ReadDeltaUInt64(ulong valueA)
|
||||
{
|
||||
bool bLargerThanA = ReadBoolean();
|
||||
ulong diff = ReadUnsignedPackedWhole();
|
||||
|
||||
return bLargerThanA ? valueA + diff : valueA - diff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new result by reading and applying a difference to a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
private long ReadDifference8_16_32(long valueA)
|
||||
{
|
||||
long diff = ReadSignedPackedWhole();
|
||||
return valueA + diff;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Single.
|
||||
/// <summary>
|
||||
/// Reads a value.
|
||||
/// </summary>
|
||||
public float ReadDeltaSingle(UDeltaPrecisionType dpt, bool unsigned)
|
||||
{
|
||||
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt8Unpacked() / (float)DOUBLE_ACCURACY;
|
||||
else
|
||||
return ReadInt8Unpacked() / (float)DOUBLE_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt16Unpacked() / (float)DOUBLE_ACCURACY;
|
||||
else
|
||||
return ReadInt16Unpacked() / (float)DOUBLE_ACCURACY;
|
||||
}
|
||||
// Everything else is unpacked.
|
||||
else
|
||||
{
|
||||
return ReadSingleUnpacked();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
public float ReadDeltaSingle(UDeltaPrecisionType dpt, float valueA, bool unsigned)
|
||||
{
|
||||
float diff = ReadDeltaSingle(dpt, unsigned);
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger);
|
||||
return bLargerThanA ? valueA + diff : valueA - diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
return valueA + diff;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
public float ReadDeltaSingle(float valueA)
|
||||
{
|
||||
const bool unsigned = false;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaSingle(dpt, valueA, unsigned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public float ReadUDeltaSingle(float valueA)
|
||||
{
|
||||
const bool unsigned = true;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaSingle(dpt, valueA, unsigned);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Double.
|
||||
/// <summary>
|
||||
/// Reads a value.
|
||||
/// </summary>
|
||||
public double ReadDeltaDouble(UDeltaPrecisionType dpt, bool unsigned)
|
||||
{
|
||||
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt8Unpacked() / DOUBLE_ACCURACY;
|
||||
else
|
||||
return ReadInt8Unpacked() / DOUBLE_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt16Unpacked() / DOUBLE_ACCURACY;
|
||||
else
|
||||
return ReadInt16Unpacked() / DOUBLE_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt32))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt32Unpacked() / DOUBLE_ACCURACY;
|
||||
else
|
||||
return ReadInt32Unpacked() / DOUBLE_ACCURACY;
|
||||
}
|
||||
// Unpacked.
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.Unset))
|
||||
{
|
||||
return ReadDoubleUnpacked();
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkManager.LogError($"Unhandled precision type of {dpt}.");
|
||||
return 0d;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
public double ReadDeltaDouble(UDeltaPrecisionType dpt, double valueA, bool unsigned)
|
||||
{
|
||||
double diff = ReadDeltaDouble(dpt, unsigned);
|
||||
// 8.
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger);
|
||||
return bLargerThanA ? valueA + diff : valueA - diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
return valueA + diff;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
public double ReadDeltaDouble(double valueA)
|
||||
{
|
||||
const bool unsigned = false;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaDouble(dpt, valueA, unsigned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public double ReadUDeltaDouble(double valueA)
|
||||
{
|
||||
const bool unsigned = true;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaDouble(dpt, valueA, unsigned);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Decimal.
|
||||
/// <summary>
|
||||
/// Reads a value.
|
||||
/// </summary>
|
||||
public decimal ReadDeltaDecimal(UDeltaPrecisionType dpt, bool unsigned)
|
||||
{
|
||||
if (dpt.FastContains(UDeltaPrecisionType.UInt8))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt8Unpacked() / DECIMAL_ACCURACY;
|
||||
else
|
||||
return ReadInt8Unpacked() / DECIMAL_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt16))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt16Unpacked() / DECIMAL_ACCURACY;
|
||||
else
|
||||
return ReadInt16Unpacked() / DECIMAL_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt32))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt32Unpacked() / DECIMAL_ACCURACY;
|
||||
else
|
||||
return ReadInt32Unpacked() / DECIMAL_ACCURACY;
|
||||
}
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.UInt64))
|
||||
{
|
||||
if (unsigned)
|
||||
return ReadUInt64Unpacked() / DECIMAL_ACCURACY;
|
||||
else
|
||||
return ReadInt64Unpacked() / DECIMAL_ACCURACY;
|
||||
}
|
||||
// Unpacked.
|
||||
else if (dpt.FastContains(UDeltaPrecisionType.Unset))
|
||||
{
|
||||
return ReadDecimalUnpacked();
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkManager.LogError($"Unhandled precision type of {dpt}.");
|
||||
return 0m;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
public decimal ReadDeltaDecimal(UDeltaPrecisionType dpt, decimal valueA, bool unsigned)
|
||||
{
|
||||
decimal diff = ReadDeltaDecimal(dpt, unsigned);
|
||||
|
||||
if (unsigned)
|
||||
{
|
||||
bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger);
|
||||
return bLargerThanA ? valueA + diff : valueA - diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
return valueA + diff;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public decimal ReadDeltaDecimal(decimal valueA)
|
||||
{
|
||||
const bool unsigned = false;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaDecimal(dpt, valueA, unsigned);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public decimal ReadUDeltaDecimal(decimal valueA)
|
||||
{
|
||||
const bool unsigned = true;
|
||||
UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked();
|
||||
|
||||
return ReadDeltaDecimal(dpt, valueA, unsigned);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FishNet Types.
|
||||
/// <summary>
|
||||
/// Reads a delta value.
|
||||
/// </summary>
|
||||
/// <returns>True if written.</returns>
|
||||
[DefaultDeltaReader]
|
||||
public NetworkBehaviour WriteDeltaNetworkBehaviour(NetworkBehaviour valueA)
|
||||
{
|
||||
return ReadNetworkBehaviour();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unity.
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// (not really for Quaternion).
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public Quaternion ReadDeltaQuaternion(Quaternion valueA, float precision = Writer.QUATERNION_PRECISION) => QuaternionDeltaPrecisionCompression.Decompress(this, valueA, precision);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public Vector2 ReadDeltaVector2(Vector2 valueA)
|
||||
{
|
||||
byte allFlags = ReadUInt8Unpacked();
|
||||
|
||||
if ((allFlags & 1) == 1)
|
||||
valueA.x = ReadUDeltaSingle(valueA.x);
|
||||
if ((allFlags & 2) == 2)
|
||||
valueA.y = ReadUDeltaSingle(valueA.y);
|
||||
|
||||
return valueA;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a difference, appending it onto a value.
|
||||
/// </summary>
|
||||
[DefaultDeltaReader]
|
||||
public Vector3 ReadDeltaVector3(Vector3 valueA)
|
||||
{
|
||||
byte allFlags = ReadUInt8Unpacked();
|
||||
|
||||
if ((allFlags & 1) == 1)
|
||||
valueA.x = ReadUDeltaSingle(valueA.x);
|
||||
if ((allFlags & 2) == 2)
|
||||
valueA.y = ReadUDeltaSingle(valueA.y);
|
||||
if ((allFlags & 4) == 4)
|
||||
valueA.z = ReadUDeltaSingle(valueA.z);
|
||||
|
||||
return valueA;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Prediction.
|
||||
/// <summary>
|
||||
/// Reads a reconcile.
|
||||
/// </summary>
|
||||
internal T ReadDeltaReconcile<T>(T lastReconcile) => ReadDelta(lastReconcile);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a replicate.
|
||||
/// </summary>
|
||||
internal int ReadDeltaReplicate<T>(T lastReadReplicate, ref T[] collection, uint tick) where T : IReplicateData
|
||||
{
|
||||
int startRemaining = Remaining;
|
||||
|
||||
// Number of entries written.
|
||||
int count = (int)ReadUInt8Unpacked();
|
||||
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);
|
||||
|
||||
uint lastReadTick = lastReadReplicate.GetTick();
|
||||
|
||||
T prev = lastReadReplicate;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Tick read is for.
|
||||
uint readTick = tick + (uint)i;
|
||||
/* If readTick is equal or lesser than lastReadReplicate
|
||||
* then there is no reason to process the data other than getting
|
||||
* it out of the reader. */
|
||||
if (readTick <= lastReadTick)
|
||||
{
|
||||
ReadDelta(prev);
|
||||
}
|
||||
else
|
||||
{
|
||||
T value = ReadDelta(prev);
|
||||
// Apply tick.
|
||||
value.SetTick(readTick);
|
||||
// Assign to collection.
|
||||
collection[i] = value;
|
||||
// Update previous.
|
||||
prev = value;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Generic.
|
||||
/// <summary>
|
||||
/// Reads a delta of any time.
|
||||
/// </summary>
|
||||
public T ReadDelta<T>(T prev)
|
||||
{
|
||||
Func<Reader, T, T> del = GenericDeltaReader<T>.Read;
|
||||
|
||||
if (del == null)
|
||||
{
|
||||
NetworkManager.LogError($"Read delta method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer.");
|
||||
return default;
|
||||
}
|
||||
else
|
||||
{
|
||||
return del.Invoke(this, prev);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90549173680239a48a3b0b61ecd47a77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Reader.Delta.cs
|
||||
uploadId: 866910
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 680939c6cee93b64ba149da2029f4308
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Reader.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,16 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object;
|
||||
using FishNet.Serializing.Helping;
|
||||
using FishNet.Transporting;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions to Read methods. Used by Read<T>.
|
||||
/// </summary>
|
||||
[APIExclude]
|
||||
public static class ReaderExtensions { }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abcc77fe436138b4082ee27da3055bb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,81 @@
|
||||
using FishNet.Managing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Reader which is reused to save on garbage collection and performance.
|
||||
/// </summary>
|
||||
public sealed class PooledReader : Reader // , IResettable
|
||||
{
|
||||
public PooledReader() { }
|
||||
internal PooledReader(byte[] bytes, NetworkManager networkManager, DataSource source = DataSource.Unset) : base(bytes, networkManager, null, source) { }
|
||||
internal PooledReader(ArraySegment<byte> segment, NetworkManager networkManager, DataSource source = DataSource.Unset) : base(segment, networkManager, null, source) { }
|
||||
public void Store() => ReaderPool.Store(this);
|
||||
|
||||
[Obsolete("Use Clear instead.")]
|
||||
public void ResetState() => Clear();
|
||||
|
||||
[Obsolete("This does not function.")]
|
||||
public void InitializeState() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collection of PooledReader. Stores and gets PooledReader.
|
||||
/// </summary>
|
||||
public static class ReaderPool
|
||||
{
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Pool of readers.
|
||||
/// </summary>
|
||||
private static readonly Stack<PooledReader> _pool = new();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Get the next reader in the pool
|
||||
/// <para>If pool is empty, creates a new Reader</para>
|
||||
/// </summary>
|
||||
public static PooledReader Retrieve(byte[] bytes, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset)
|
||||
{
|
||||
return Retrieve(new ArraySegment<byte>(bytes), networkManager, source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the next reader in the pool or creates a new one if none are available.
|
||||
/// </summary>
|
||||
public static PooledReader Retrieve(ArraySegment<byte> segment, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset)
|
||||
{
|
||||
PooledReader result;
|
||||
if (_pool.TryPop(out result))
|
||||
result.Initialize(segment, networkManager, source);
|
||||
else
|
||||
result = new(segment, networkManager, source);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts reader back into pool
|
||||
/// </summary>
|
||||
public static void Store(PooledReader reader)
|
||||
{
|
||||
_pool.Push(reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts reader back into pool if not null, and nullifies source reference.
|
||||
/// </summary>
|
||||
public static void StoreAndDefault(ref PooledReader reader)
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
_pool.Push(reader);
|
||||
reader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 318b117dd2ebd1b4b9e2021796b45eee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/ReaderPool.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,2 @@
|
||||
// Remove in V5
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1f98beaf8f697d4b8bb1e6b6ef32d42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/ReaderStatics.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace FishNet.Serializing.Helping
|
||||
{
|
||||
internal sealed class SceneHandleEqualityComparer : EqualityComparer<Scene>
|
||||
{
|
||||
public override bool Equals(Scene a, Scene b)
|
||||
{
|
||||
return a.handle == b.handle;
|
||||
}
|
||||
|
||||
public override int GetHashCode(Scene obj)
|
||||
{
|
||||
return obj.handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 161dbbe3995ff53479fc4e259f86549d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/SceneComparer.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70577ba991466c849818d6af55bb4f9f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Reader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a substream. Start reading from it with StartReading method.
|
||||
/// </summary>
|
||||
/// <returns>Returns SubStream</returns>
|
||||
public SubStream ReadSubStream()
|
||||
{
|
||||
// read length of subStream
|
||||
int streamLength = ReadInt32();
|
||||
|
||||
// if length is -1, it is invalid
|
||||
if (streamLength == SubStream.UNINITIALIZED_LENGTH)
|
||||
{
|
||||
// returns Uninitialized SubStream
|
||||
return SubStream.GetUninitialized();
|
||||
}
|
||||
|
||||
return SubStream.CreateFromReader(this, streamLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 286543df642a48448ae2ef943576e242
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using FishNet.Managing;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Special reader/writer buffer struct that can be used in Fishnet RPCs or Broadcasts, as arguments or part of structs
|
||||
/// Use cases:
|
||||
/// - replacement for stream sort of
|
||||
/// - instead of always allocating some arrays T[] and sending that over RPCs/Broadcast, you can use SubStream
|
||||
/// - you can pass SubStream into objects via reference 'ref', and those objects write/read state, useful for dynamic length reconcile (items, inventory, buffs, etc...)
|
||||
/// - sending data inside OnServerSpawn to clients via TargetRPC
|
||||
/// - instead of writting custom serializers for big struct, you can use SubStream inside RPCs/Broadcasts
|
||||
/// Pros:
|
||||
/// - reading is zero copy, reads directly from FishNet buffers
|
||||
/// - everything is pooled
|
||||
/// - ease of use
|
||||
/// - SubStream can also be left uninitialized (default)
|
||||
/// - Can work safely with multiple receivers in Broadcasts, as long as you read data in the same order
|
||||
/// Cons:
|
||||
/// - no reading over length protection, you have to know how much data you are reading, due to buffer being red can be larger than substreams buffer
|
||||
/// - writing buffers are also pooled, but there is a copy (since you write into it, then what is written is copied into fishnet internal buffer, but it's byte copy (fast)
|
||||
/// - have to use Dispose() to return buffers to pool, or it may result in memory leak
|
||||
/// - reading in multiple receiver methods (for same client) in Broadcasts, you have extra deserialization processing per each method
|
||||
/// - might be unsafe to use this to send from clients (undefined data length), but so is sending T[] or List
|
||||
/// <T>
|
||||
/// from clients
|
||||
/// - not to be used for IReplicateData/input structs, because underlying reading buffer may be changed where as IReplicateData structs are stored internally in replay buffer (substream buffer is not)
|
||||
/// Note:
|
||||
/// - If you write/read custom structs ONLY via SubStream, automatic serializer will not pick those up. Mark those custom structs with [FishNet.CodeGenerating.IncludeSerialization].
|
||||
/// Codegen detects only custom structs that are used in RPC/Broadcast methods, not in SubStream.
|
||||
/// </summary>
|
||||
public struct SubStream : IResettable
|
||||
{
|
||||
/// <summary>
|
||||
/// Is Substream initialized (can be read from or written to)
|
||||
/// </summary>
|
||||
public bool Initialized { get; private set; }
|
||||
/// <summary>
|
||||
/// Returns Length of substream data
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_writer != null)
|
||||
return _writer.Length;
|
||||
if (_reader != null)
|
||||
return _reader.Length;
|
||||
|
||||
return UNINITIALIZED_LENGTH;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns remaining bytes to read from substream
|
||||
/// </summary>
|
||||
public int Remaining => _reader != null ? _reader.Remaining : UNINITIALIZED_LENGTH;
|
||||
/// <summary>
|
||||
/// Returns NetworkManager that Substream was initialized with
|
||||
/// </summary>
|
||||
public NetworkManager NetworkManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_writer != null)
|
||||
return _writer.NetworkManager;
|
||||
if (_reader != null)
|
||||
return _reader.NetworkManager;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private PooledReader _reader;
|
||||
private int _startPosition;
|
||||
private PooledWriter _writer;
|
||||
private bool _disposed;
|
||||
/// <summary>
|
||||
/// Length to use when SubStream is not initialized.
|
||||
/// </summary>
|
||||
public const int UNINITIALIZED_LENGTH = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Creates SubStream for writing, use this before sending into RPC or Broadcast
|
||||
/// </summary>
|
||||
/// <param name = "manager">Need to include network manager for handling of networked IDs</param>
|
||||
/// <param name = "minimumLength">Minimum expected length of data, that will be written</param>
|
||||
/// <returns>Returns writer of SubStream</returns>
|
||||
public static SubStream StartWriting(NetworkManager manager, out PooledWriter writer, int minimumLength = 0)
|
||||
{
|
||||
if (minimumLength == 0)
|
||||
writer = WriterPool.Retrieve(manager);
|
||||
else
|
||||
writer = WriterPool.Retrieve(manager, minimumLength);
|
||||
|
||||
SubStream stream = new()
|
||||
{
|
||||
_writer = writer,
|
||||
Initialized = true
|
||||
};
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts reading from substream via Reader class. Do not forget do Dispose() after reading
|
||||
/// </summary>
|
||||
/// <param name = "reader">Reader to read data from</param>
|
||||
/// <returns>Returns true, if SubStream is initialized else false</returns>
|
||||
public bool StartReading(out Reader reader)
|
||||
{
|
||||
if (Initialized)
|
||||
{
|
||||
// reset reader, in case we are reading in multiple broadcasts delegates/events
|
||||
_reader.Position = _startPosition;
|
||||
reader = _reader;
|
||||
return true;
|
||||
}
|
||||
reader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static SubStream CreateFromReader(Reader originalReader, int subStreamLength)
|
||||
{
|
||||
if (subStreamLength < 0)
|
||||
{
|
||||
originalReader.NetworkManager.LogError("SubStream length cannot be less than 0");
|
||||
return default;
|
||||
}
|
||||
|
||||
byte[] originalReaderBuffer = originalReader.GetBuffer();
|
||||
|
||||
// inherits reading buffer directly from fishnet reader
|
||||
ArraySegment<byte> arraySegment = new(originalReaderBuffer, originalReader.Position, subStreamLength);
|
||||
|
||||
PooledReader newReader = ReaderPool.Retrieve(arraySegment, originalReader.NetworkManager);
|
||||
|
||||
// advance original reader by length of substream data
|
||||
originalReader.Skip(subStreamLength);
|
||||
|
||||
return new()
|
||||
{
|
||||
_startPosition = newReader.Position,
|
||||
_reader = newReader,
|
||||
_writer = null,
|
||||
_disposed = false,
|
||||
Initialized = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets reader to start position, so you can read data again from start of substream.
|
||||
/// </summary>
|
||||
/// <exception cref = "ArgumentException"></exception>
|
||||
public void ResetReaderToStartPosition()
|
||||
{
|
||||
if (_reader != null)
|
||||
_reader.Position = _startPosition;
|
||||
else
|
||||
NetworkManager.LogError("SubStream was not initialized as reader!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to get writer of SubStream
|
||||
/// </summary>
|
||||
/// <exception cref = "ArgumentException"></exception>
|
||||
internal PooledWriter GetWriter()
|
||||
{
|
||||
if (!Initialized)
|
||||
NetworkManager.LogError("SubStream was not initialized, it has to be initialized properly either localy or remotely!");
|
||||
else if (_writer == null)
|
||||
NetworkManager.LogError($"GetWriter() requires SubStream to be initialized as writer! You have to create SubStream with {nameof(StartWriting)}()!");
|
||||
|
||||
return _writer;
|
||||
}
|
||||
|
||||
internal PooledReader GetReader()
|
||||
{
|
||||
if (!Initialized)
|
||||
NetworkManager.LogError("SubStream was not initialized, it has to be initialized properly either localy or remotely!");
|
||||
if (_reader == null)
|
||||
NetworkManager.LogError($"GetReader() requires SubStream to be initialized as reader!");
|
||||
|
||||
return _reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns uninitialized SubStream. Can send safely over network, but cannot be read from (StartReading will return false).
|
||||
/// You can also use 'var stream = default;' instead.
|
||||
/// </summary>
|
||||
/// <returns>Empty SubStream</returns>
|
||||
internal static SubStream GetUninitialized()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Initialized = false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do not forget to call this after:
|
||||
/// - you stopped writing to Substream AND already sent it via RPCs/Broadcasts
|
||||
/// - you stoped reading from it inside RPCs/Broadcast receive event
|
||||
/// - if you use it in Reconcile method, you have dispose SubStream inside Dispose() of IReconcileData struct
|
||||
/// </summary>
|
||||
public void ResetState()
|
||||
{
|
||||
if (!_disposed) // dispose reader only once
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
if (_reader != null)
|
||||
{
|
||||
_reader.Store();
|
||||
_reader = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_writer != null)
|
||||
{
|
||||
if (_writer.Length < WriterPool.LENGTH_BRACKET) // 1000 is LENGTH_BRACKET
|
||||
_writer.Store();
|
||||
else
|
||||
_writer.StoreLength();
|
||||
|
||||
_writer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 384099d97a6ecd148a73ef36bed6461e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a SubStream.
|
||||
/// </summary>
|
||||
/// <param name = "value">Substream</param>
|
||||
public void WriteSubStream(SubStream value)
|
||||
{
|
||||
// Uninitialized substream, write Length as -1
|
||||
if (!value.Initialized)
|
||||
{
|
||||
WriteInt32(SubStream.UNINITIALIZED_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
PooledWriter bufferWriter = value.GetWriter();
|
||||
|
||||
// Write length and data
|
||||
WriteInt32(bufferWriter.Length);
|
||||
WriteUInt8Array(bufferWriter.GetBuffer(), 0, bufferWriter.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f86fddaee91a2f246b421c95475e700f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
[System.Serializable]
|
||||
internal class TransformPackingData
|
||||
{
|
||||
public AutoPackType Position = AutoPackType.Packed;
|
||||
public AutoPackType Rotation = AutoPackType.Packed;
|
||||
public AutoPackType Scale = AutoPackType.Packed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80a80dabe02daf6428cce0f16ea49877
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/TransformPackingData.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aaaca3090257be40b80f33b9e955446
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writebool2(bool2 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.x)
|
||||
b |= 1;
|
||||
if (value.y)
|
||||
b |= 2;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool3(bool3 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.x)
|
||||
b |= 1;
|
||||
if (value.y)
|
||||
b |= 2;
|
||||
if (value.z)
|
||||
b |= 4;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool4(bool4 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.x)
|
||||
b |= 1;
|
||||
if (value.y)
|
||||
b |= 2;
|
||||
if (value.z)
|
||||
b |= 4;
|
||||
if (value.w)
|
||||
b |= 8;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool2x2(bool2x2 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
b |= 1;
|
||||
if (value.c0.y)
|
||||
b |= 2;
|
||||
if (value.c1.x)
|
||||
b |= 4;
|
||||
if (value.c1.y)
|
||||
b |= 8;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool2x3(bool2x3 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
b |= 1;
|
||||
if (value.c0.y)
|
||||
b |= 2;
|
||||
if (value.c1.x)
|
||||
b |= 4;
|
||||
if (value.c1.y)
|
||||
b |= 8;
|
||||
if (value.c2.x)
|
||||
b |= 16;
|
||||
if (value.c2.y)
|
||||
b |= 32;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool2x4(bool2x4 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
b |= 1;
|
||||
if (value.c0.y)
|
||||
b |= 2;
|
||||
if (value.c1.x)
|
||||
b |= 4;
|
||||
if (value.c1.y)
|
||||
b |= 8;
|
||||
if (value.c2.x)
|
||||
b |= 16;
|
||||
if (value.c2.y)
|
||||
b |= 32;
|
||||
if (value.c3.x)
|
||||
b |= 64;
|
||||
if (value.c3.y)
|
||||
b |= 128;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool3x2(bool3x2 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
b |= 1;
|
||||
if (value.c0.y)
|
||||
b |= 2;
|
||||
if (value.c0.z)
|
||||
b |= 4;
|
||||
if (value.c1.x)
|
||||
b |= 8;
|
||||
if (value.c1.y)
|
||||
b |= 16;
|
||||
if (value.c1.z)
|
||||
b |= 32;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool3x3(bool3x3 value)
|
||||
{
|
||||
ushort s = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
s |= 1;
|
||||
if (value.c0.y)
|
||||
s |= 2;
|
||||
if (value.c0.z)
|
||||
s |= 4;
|
||||
if (value.c1.x)
|
||||
s |= 8;
|
||||
if (value.c1.y)
|
||||
s |= 16;
|
||||
if (value.c1.z)
|
||||
s |= 32;
|
||||
if (value.c2.x)
|
||||
s |= 64;
|
||||
if (value.c2.y)
|
||||
s |= 128;
|
||||
if (value.c2.z)
|
||||
s |= 256;
|
||||
|
||||
WriteUInt16(s);
|
||||
}
|
||||
|
||||
public void Writebool3x4(bool3x4 value)
|
||||
{
|
||||
ushort s = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
s |= 1;
|
||||
if (value.c0.y)
|
||||
s |= 2;
|
||||
if (value.c0.z)
|
||||
s |= 4;
|
||||
if (value.c1.x)
|
||||
s |= 8;
|
||||
if (value.c1.y)
|
||||
s |= 16;
|
||||
if (value.c1.z)
|
||||
s |= 32;
|
||||
if (value.c2.x)
|
||||
s |= 64;
|
||||
if (value.c2.y)
|
||||
s |= 128;
|
||||
if (value.c2.z)
|
||||
s |= 256;
|
||||
if (value.c3.x)
|
||||
s |= 512;
|
||||
if (value.c3.y)
|
||||
s |= 1024;
|
||||
if (value.c3.z)
|
||||
s |= 2048;
|
||||
|
||||
WriteUInt16(s);
|
||||
}
|
||||
|
||||
public void Writebool4x2(bool4x2 value)
|
||||
{
|
||||
byte b = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
b |= 1;
|
||||
if (value.c0.y)
|
||||
b |= 2;
|
||||
if (value.c0.z)
|
||||
b |= 4;
|
||||
if (value.c0.w)
|
||||
b |= 8;
|
||||
if (value.c1.x)
|
||||
b |= 16;
|
||||
if (value.c1.y)
|
||||
b |= 32;
|
||||
if (value.c1.z)
|
||||
b |= 64;
|
||||
if (value.c1.w)
|
||||
b |= 128;
|
||||
|
||||
WriteUInt8Unpacked(b);
|
||||
}
|
||||
|
||||
public void Writebool4x3(bool4x3 value)
|
||||
{
|
||||
ushort s = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
s |= 1;
|
||||
if (value.c0.y)
|
||||
s |= 2;
|
||||
if (value.c0.z)
|
||||
s |= 4;
|
||||
if (value.c0.w)
|
||||
s |= 8;
|
||||
if (value.c1.x)
|
||||
s |= 16;
|
||||
if (value.c1.y)
|
||||
s |= 32;
|
||||
if (value.c1.z)
|
||||
s |= 64;
|
||||
if (value.c1.w)
|
||||
s |= 128;
|
||||
if (value.c2.x)
|
||||
s |= 256;
|
||||
if (value.c2.y)
|
||||
s |= 512;
|
||||
if (value.c2.z)
|
||||
s |= 1024;
|
||||
if (value.c2.w)
|
||||
s |= 2048;
|
||||
|
||||
WriteUInt16(s);
|
||||
}
|
||||
|
||||
public void Writebool4x4(bool4x4 value)
|
||||
{
|
||||
ushort s = 0;
|
||||
|
||||
if (value.c0.x)
|
||||
s |= 1;
|
||||
if (value.c0.y)
|
||||
s |= 2;
|
||||
if (value.c0.z)
|
||||
s |= 4;
|
||||
if (value.c0.w)
|
||||
s |= 8;
|
||||
if (value.c1.x)
|
||||
s |= 16;
|
||||
if (value.c1.y)
|
||||
s |= 32;
|
||||
if (value.c1.z)
|
||||
s |= 64;
|
||||
if (value.c1.w)
|
||||
s |= 128;
|
||||
if (value.c2.x)
|
||||
s |= 256;
|
||||
if (value.c2.y)
|
||||
s |= 512;
|
||||
if (value.c2.z)
|
||||
s |= 1024;
|
||||
if (value.c2.w)
|
||||
s |= 2048;
|
||||
if (value.c3.x)
|
||||
s |= 4096;
|
||||
if (value.c3.y)
|
||||
s |= 8192;
|
||||
if (value.c3.z)
|
||||
s |= 16384;
|
||||
if (value.c3.w)
|
||||
s |= 32768;
|
||||
|
||||
WriteUInt16(s);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public bool2 Readbool2()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
return new bool2() { x = (b & 1) != 0, y = (b & 2) != 0 };
|
||||
}
|
||||
|
||||
public bool3 Readbool3()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
return new bool3()
|
||||
{
|
||||
x = (b & 1) != 0,
|
||||
y = (b & 2) != 0,
|
||||
z = (b & 4) != 0
|
||||
};
|
||||
}
|
||||
|
||||
public bool4 Readbool4()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
return new bool4
|
||||
{
|
||||
x = (b & 1) != 0,
|
||||
y = (b & 2) != 0,
|
||||
z = (b & 4) != 0,
|
||||
w = (b & 8) != 0
|
||||
};
|
||||
}
|
||||
|
||||
public bool2x2 Readbool2x2()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
bool2x2 value = default;
|
||||
|
||||
value.c0.x = (b & 1) != 0;
|
||||
value.c0.y = (b & 2) != 0;
|
||||
value.c1.x = (b & 4) != 0;
|
||||
value.c1.y = (b & 8) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool2x3 Readbool2x3()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
bool2x3 value = default;
|
||||
|
||||
value.c0.x = (b & 1) != 0;
|
||||
value.c0.y = (b & 2) != 0;
|
||||
value.c1.x = (b & 4) != 0;
|
||||
value.c1.y = (b & 8) != 0;
|
||||
value.c2.x = (b & 16) != 0;
|
||||
value.c2.y = (b & 32) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool2x4 Readbool2x4()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
bool2x4 value = default;
|
||||
|
||||
value.c0.x = (b & 1) != 0;
|
||||
value.c0.y = (b & 2) != 0;
|
||||
value.c1.x = (b & 4) != 0;
|
||||
value.c1.y = (b & 8) != 0;
|
||||
value.c2.x = (b & 16) != 0;
|
||||
value.c2.y = (b & 32) != 0;
|
||||
value.c3.x = (b & 64) != 0;
|
||||
value.c3.y = (b & 128) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool3x2 Readbool3x2()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
bool3x2 value = default;
|
||||
|
||||
value.c0.x = (b & 1) != 0;
|
||||
value.c0.y = (b & 2) != 0;
|
||||
value.c0.z = (b & 4) != 0;
|
||||
value.c1.x = (b & 8) != 0;
|
||||
value.c1.y = (b & 16) != 0;
|
||||
value.c1.z = (b & 32) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool3x3 Readbool3x3()
|
||||
{
|
||||
ushort s = ReadUInt16();
|
||||
|
||||
bool3x3 value = default;
|
||||
value.c0.x = (s & 1) != 0;
|
||||
value.c0.y = (s & 2) != 0;
|
||||
value.c0.z = (s & 4) != 0;
|
||||
value.c1.x = (s & 8) != 0;
|
||||
value.c1.y = (s & 16) != 0;
|
||||
value.c1.z = (s & 32) != 0;
|
||||
value.c2.x = (s & 64) != 0;
|
||||
value.c2.y = (s & 128) != 0;
|
||||
value.c2.z = (s & 256) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool3x4 Readbool3x4()
|
||||
{
|
||||
ushort s = ReadUInt16();
|
||||
|
||||
bool3x4 value = default;
|
||||
|
||||
value.c0.x = (s & 1) != 0;
|
||||
value.c0.y = (s & 2) != 0;
|
||||
value.c0.z = (s & 4) != 0;
|
||||
value.c1.x = (s & 8) != 0;
|
||||
value.c1.y = (s & 16) != 0;
|
||||
value.c1.z = (s & 32) != 0;
|
||||
value.c2.x = (s & 64) != 0;
|
||||
value.c2.y = (s & 128) != 0;
|
||||
value.c2.z = (s & 256) != 0;
|
||||
value.c3.x = (s & 512) != 0;
|
||||
value.c3.y = (s & 1024) != 0;
|
||||
value.c3.z = (s & 2048) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool4x2 Readbool4x2()
|
||||
{
|
||||
byte b = ReadUInt8Unpacked();
|
||||
|
||||
bool4x2 value = default;
|
||||
|
||||
value.c0.x = (b & 1) != 0;
|
||||
value.c0.y = (b & 2) != 0;
|
||||
value.c0.z = (b & 4) != 0;
|
||||
value.c0.w = (b & 8) != 0;
|
||||
value.c1.x = (b & 16) != 0;
|
||||
value.c1.y = (b & 32) != 0;
|
||||
value.c1.z = (b & 64) != 0;
|
||||
value.c1.w = (b & 128) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool4x3 Readbool4x3()
|
||||
{
|
||||
ushort s = ReadUInt16();
|
||||
|
||||
bool4x3 value = default;
|
||||
|
||||
value.c0.x = (s & 1) != 0;
|
||||
value.c0.y = (s & 2) != 0;
|
||||
value.c0.z = (s & 4) != 0;
|
||||
value.c0.w = (s & 8) != 0;
|
||||
value.c1.x = (s & 16) != 0;
|
||||
value.c1.y = (s & 32) != 0;
|
||||
value.c1.z = (s & 64) != 0;
|
||||
value.c1.w = (s & 128) != 0;
|
||||
value.c2.x = (s & 256) != 0;
|
||||
value.c2.y = (s & 512) != 0;
|
||||
value.c2.z = (s & 1024) != 0;
|
||||
value.c2.w = (s & 2048) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool4x4 Readbool4x4()
|
||||
{
|
||||
ushort s = ReadUInt16();
|
||||
|
||||
bool4x4 value = default;
|
||||
|
||||
value.c0.x = (s & 1) != 0;
|
||||
value.c0.y = (s & 2) != 0;
|
||||
value.c0.z = (s & 4) != 0;
|
||||
value.c0.w = (s & 8) != 0;
|
||||
value.c1.x = (s & 16) != 0;
|
||||
value.c1.y = (s & 32) != 0;
|
||||
value.c1.z = (s & 64) != 0;
|
||||
value.c1.w = (s & 128) != 0;
|
||||
value.c2.x = (s & 256) != 0;
|
||||
value.c2.y = (s & 512) != 0;
|
||||
value.c2.z = (s & 1024) != 0;
|
||||
value.c2.w = (s & 2048) != 0;
|
||||
value.c3.x = (s & 4096) != 0;
|
||||
value.c3.y = (s & 8192) != 0;
|
||||
value.c3.z = (s & 16384) != 0;
|
||||
value.c3.w = (s & 32768) != 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 254b9133ed0260b4685ea1d28bd15df1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs
|
||||
uploadId: 866910
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writedouble2(double2 value)
|
||||
{
|
||||
WriteDouble(value.x);
|
||||
WriteDouble(value.y);
|
||||
}
|
||||
|
||||
public void Writedouble3(double3 value)
|
||||
{
|
||||
WriteDouble(value.x);
|
||||
WriteDouble(value.y);
|
||||
WriteDouble(value.z);
|
||||
}
|
||||
|
||||
public void Writedouble4(double4 value)
|
||||
{
|
||||
WriteDouble(value.x);
|
||||
WriteDouble(value.y);
|
||||
WriteDouble(value.z);
|
||||
WriteDouble(value.w);
|
||||
}
|
||||
|
||||
public void Writedouble2x2(double2x2 value)
|
||||
{
|
||||
Writedouble2(value.c0);
|
||||
Writedouble2(value.c1);
|
||||
}
|
||||
|
||||
public void Writedouble2x3(double2x3 value)
|
||||
{
|
||||
Writedouble2(value.c0);
|
||||
Writedouble2(value.c1);
|
||||
Writedouble2(value.c2);
|
||||
}
|
||||
|
||||
public void Writedouble2x4(double2x4 value)
|
||||
{
|
||||
Writedouble2(value.c0);
|
||||
Writedouble2(value.c1);
|
||||
Writedouble2(value.c2);
|
||||
Writedouble2(value.c3);
|
||||
}
|
||||
|
||||
public void Writedouble3x2(double3x2 value)
|
||||
{
|
||||
Writedouble3(value.c0);
|
||||
Writedouble3(value.c1);
|
||||
}
|
||||
|
||||
public void Writedouble4x2(double4x2 value)
|
||||
{
|
||||
Writedouble4(value.c0);
|
||||
Writedouble4(value.c1);
|
||||
}
|
||||
|
||||
public void Writedouble3x4(double3x4 value)
|
||||
{
|
||||
Writedouble3(value.c0);
|
||||
Writedouble3(value.c1);
|
||||
Writedouble3(value.c2);
|
||||
Writedouble3(value.c3);
|
||||
}
|
||||
|
||||
public void Writedouble4x3(double4x3 value)
|
||||
{
|
||||
Writedouble4(value.c0);
|
||||
Writedouble4(value.c1);
|
||||
Writedouble4(value.c2);
|
||||
}
|
||||
|
||||
public void Writedouble3x3(double3x3 value)
|
||||
{
|
||||
Writedouble3(value.c0);
|
||||
Writedouble3(value.c1);
|
||||
Writedouble3(value.c2);
|
||||
}
|
||||
|
||||
public void Writedouble4x4(double4x4 value)
|
||||
{
|
||||
Writedouble4(value.c0);
|
||||
Writedouble4(value.c1);
|
||||
Writedouble4(value.c2);
|
||||
Writedouble4(value.c3);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public double2 Readdouble2()
|
||||
{
|
||||
return new double2
|
||||
{
|
||||
x = ReadDouble(),
|
||||
y = ReadDouble()
|
||||
};
|
||||
}
|
||||
|
||||
public double3 Readdouble3()
|
||||
{
|
||||
return new double3()
|
||||
{
|
||||
x = ReadDouble(),
|
||||
y = ReadDouble(),
|
||||
z = ReadDouble()
|
||||
};
|
||||
}
|
||||
|
||||
public double4 Readdouble4()
|
||||
{
|
||||
return new double4()
|
||||
{
|
||||
x = ReadDouble(),
|
||||
y = ReadDouble(),
|
||||
z = ReadDouble(),
|
||||
w = ReadDouble()
|
||||
};
|
||||
}
|
||||
|
||||
public double2x2 Readdouble2x2()
|
||||
{
|
||||
return new double2x2()
|
||||
{
|
||||
c0 = Readdouble2(),
|
||||
c1 = Readdouble2()
|
||||
};
|
||||
}
|
||||
|
||||
public double2x3 Readdouble2x3()
|
||||
{
|
||||
return new double2x3()
|
||||
{
|
||||
c0 = Readdouble2(),
|
||||
c1 = Readdouble2(),
|
||||
c2 = Readdouble2()
|
||||
};
|
||||
}
|
||||
|
||||
public double2x4 Readdouble2x4()
|
||||
{
|
||||
return new double2x4()
|
||||
{
|
||||
c0 = Readdouble2(),
|
||||
c1 = Readdouble2(),
|
||||
c2 = Readdouble2(),
|
||||
c3 = Readdouble2()
|
||||
};
|
||||
}
|
||||
|
||||
public double3x2 Readdouble3x2()
|
||||
{
|
||||
return new double3x2()
|
||||
{
|
||||
c0 = Readdouble3(),
|
||||
c1 = Readdouble3()
|
||||
};
|
||||
}
|
||||
|
||||
public double4x2 Readdouble4x2()
|
||||
{
|
||||
return new double4x2()
|
||||
{
|
||||
c0 = Readdouble4(),
|
||||
c1 = Readdouble4()
|
||||
};
|
||||
}
|
||||
|
||||
public double3x4 Readdouble3x4()
|
||||
{
|
||||
return new double3x4()
|
||||
{
|
||||
c0 = Readdouble3(),
|
||||
c1 = Readdouble3(),
|
||||
c2 = Readdouble3(),
|
||||
c3 = Readdouble3()
|
||||
};
|
||||
}
|
||||
|
||||
public double4x3 Readdouble4x3()
|
||||
{
|
||||
return new double4x3()
|
||||
{
|
||||
c0 = Readdouble4(),
|
||||
c1 = Readdouble4(),
|
||||
c2 = Readdouble4()
|
||||
};
|
||||
}
|
||||
|
||||
public double3x3 Readdouble3x3()
|
||||
{
|
||||
return new double3x3()
|
||||
{
|
||||
c0 = Readdouble3(),
|
||||
c1 = Readdouble3(),
|
||||
c2 = Readdouble3()
|
||||
};
|
||||
}
|
||||
|
||||
public double4x4 Readdouble4x4()
|
||||
{
|
||||
return new double4x4()
|
||||
{
|
||||
c0 = Readdouble4(),
|
||||
c1 = Readdouble4(),
|
||||
c2 = Readdouble4(),
|
||||
c3 = Readdouble4()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b02bb31d2808f94695dfa971bbe624b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs
|
||||
uploadId: 866910
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writefloat2(float2 value)
|
||||
{
|
||||
WriteSingle(value.x);
|
||||
WriteSingle(value.y);
|
||||
}
|
||||
|
||||
public void Writefloat3(float3 value)
|
||||
{
|
||||
WriteSingle(value.x);
|
||||
WriteSingle(value.y);
|
||||
WriteSingle(value.z);
|
||||
}
|
||||
|
||||
public void Writefloat4(float4 value)
|
||||
{
|
||||
WriteSingle(value.x);
|
||||
WriteSingle(value.y);
|
||||
WriteSingle(value.z);
|
||||
WriteSingle(value.w);
|
||||
}
|
||||
|
||||
public void Writefloat2x2(float2x2 value)
|
||||
{
|
||||
Writefloat2(value.c0);
|
||||
Writefloat2(value.c1);
|
||||
}
|
||||
|
||||
public void Writefloat2x3(float2x3 value)
|
||||
{
|
||||
Writefloat2(value.c0);
|
||||
Writefloat2(value.c1);
|
||||
Writefloat2(value.c2);
|
||||
}
|
||||
|
||||
public void Writefloat2x4(float2x4 value)
|
||||
{
|
||||
Writefloat2(value.c0);
|
||||
Writefloat2(value.c1);
|
||||
Writefloat2(value.c2);
|
||||
Writefloat2(value.c3);
|
||||
}
|
||||
|
||||
public void Writefloat3x2(float3x2 value)
|
||||
{
|
||||
Writefloat3(value.c0);
|
||||
Writefloat3(value.c1);
|
||||
}
|
||||
|
||||
public void Writefloat3x3(float3x3 value)
|
||||
{
|
||||
Writefloat3(value.c0);
|
||||
Writefloat3(value.c1);
|
||||
Writefloat3(value.c2);
|
||||
}
|
||||
|
||||
public void Writefloat3x4(float3x4 value)
|
||||
{
|
||||
Writefloat3(value.c0);
|
||||
Writefloat3(value.c1);
|
||||
Writefloat3(value.c2);
|
||||
Writefloat3(value.c3);
|
||||
}
|
||||
|
||||
public void Writefloat4x2(float4x2 value)
|
||||
{
|
||||
Writefloat4(value.c0);
|
||||
Writefloat4(value.c1);
|
||||
}
|
||||
|
||||
public void Writefloat4x3(float4x3 value)
|
||||
{
|
||||
Writefloat4(value.c0);
|
||||
Writefloat4(value.c1);
|
||||
Writefloat4(value.c2);
|
||||
}
|
||||
|
||||
public void Writefloat4x4(float4x4 value)
|
||||
{
|
||||
Writefloat4(value.c0);
|
||||
Writefloat4(value.c1);
|
||||
Writefloat4(value.c2);
|
||||
Writefloat4(value.c3);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public float2 Readfloat2()
|
||||
{
|
||||
return new float2
|
||||
{
|
||||
x = ReadSingle(),
|
||||
y = ReadSingle()
|
||||
};
|
||||
}
|
||||
|
||||
public float3 Readfloat3()
|
||||
{
|
||||
return new float3()
|
||||
{
|
||||
x = ReadSingle(),
|
||||
y = ReadSingle(),
|
||||
z = ReadSingle()
|
||||
};
|
||||
}
|
||||
|
||||
public float4 Readfloat4()
|
||||
{
|
||||
return new float4()
|
||||
{
|
||||
x = ReadSingle(),
|
||||
y = ReadSingle(),
|
||||
z = ReadSingle(),
|
||||
w = ReadSingle()
|
||||
};
|
||||
}
|
||||
|
||||
public float2x2 Readfloat2x2()
|
||||
{
|
||||
return new float2x2()
|
||||
{
|
||||
c0 = Readfloat2(),
|
||||
c1 = Readfloat2()
|
||||
};
|
||||
}
|
||||
|
||||
public float2x3 Readfloat2x3()
|
||||
{
|
||||
return new float2x3()
|
||||
{
|
||||
c0 = Readfloat2(),
|
||||
c1 = Readfloat2(),
|
||||
c2 = Readfloat2()
|
||||
};
|
||||
}
|
||||
|
||||
public float2x4 Readfloat2x4()
|
||||
{
|
||||
return new float2x4()
|
||||
{
|
||||
c0 = Readfloat2(),
|
||||
c1 = Readfloat2(),
|
||||
c2 = Readfloat2(),
|
||||
c3 = Readfloat2()
|
||||
};
|
||||
}
|
||||
|
||||
public float3x2 Readfloat3x2()
|
||||
{
|
||||
return new float3x2()
|
||||
{
|
||||
c0 = Readfloat3(),
|
||||
c1 = Readfloat3()
|
||||
};
|
||||
}
|
||||
|
||||
public float3x3 Readfloat3x3()
|
||||
{
|
||||
return new float3x3()
|
||||
{
|
||||
c0 = Readfloat3(),
|
||||
c1 = Readfloat3(),
|
||||
c2 = Readfloat3()
|
||||
};
|
||||
}
|
||||
|
||||
public float3x4 Readfloat3x4()
|
||||
{
|
||||
return new float3x4()
|
||||
{
|
||||
c0 = Readfloat3(),
|
||||
c1 = Readfloat3(),
|
||||
c2 = Readfloat3(),
|
||||
c3 = Readfloat3()
|
||||
};
|
||||
}
|
||||
|
||||
public float4x2 Readfloat4x2()
|
||||
{
|
||||
return new float4x2()
|
||||
{
|
||||
c0 = Readfloat4(),
|
||||
c1 = Readfloat4()
|
||||
};
|
||||
}
|
||||
|
||||
public float4x3 Readfloat4x3()
|
||||
{
|
||||
return new float4x3()
|
||||
{
|
||||
c0 = Readfloat4(),
|
||||
c1 = Readfloat4(),
|
||||
c2 = Readfloat4()
|
||||
};
|
||||
}
|
||||
|
||||
public float4x4 Readfloat4x4()
|
||||
{
|
||||
return new float4x4()
|
||||
{
|
||||
c0 = Readfloat4(),
|
||||
c1 = Readfloat4(),
|
||||
c2 = Readfloat4(),
|
||||
c3 = Readfloat4()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 501c2baf76f23f64fadc0fd46e2d95fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,77 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writehalf(half value)
|
||||
{
|
||||
WriteUInt16(value.value);
|
||||
}
|
||||
|
||||
public void Writehalf2(half2 value)
|
||||
{
|
||||
WriteUInt16(value.x.value);
|
||||
WriteUInt16(value.y.value);
|
||||
}
|
||||
|
||||
public void Writehalf3(half3 value)
|
||||
{
|
||||
WriteUInt16(value.x.value);
|
||||
WriteUInt16(value.y.value);
|
||||
WriteUInt16(value.z.value);
|
||||
}
|
||||
|
||||
public void Writehalf4(half4 value)
|
||||
{
|
||||
WriteUInt16(value.x.value);
|
||||
WriteUInt16(value.y.value);
|
||||
WriteUInt16(value.z.value);
|
||||
WriteUInt16(value.w.value);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public half Readhalf()
|
||||
{
|
||||
return new half { value = ReadUInt16() };
|
||||
}
|
||||
|
||||
public half2 Readhalf2()
|
||||
{
|
||||
half2 h = default;
|
||||
|
||||
h.x.value = ReadUInt16();
|
||||
h.y.value = ReadUInt16();
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
public half3 Readhalf3()
|
||||
{
|
||||
half3 h = default;
|
||||
|
||||
h.x.value = ReadUInt16();
|
||||
h.y.value = ReadUInt16();
|
||||
h.z.value = ReadUInt16();
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
public half4 Readhalf4()
|
||||
{
|
||||
half4 h = default;
|
||||
|
||||
h.x.value = ReadUInt16();
|
||||
h.y.value = ReadUInt16();
|
||||
h.z.value = ReadUInt16();
|
||||
h.w.value = ReadUInt16();
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeb83ce2d1531bc4ea314600a3978c79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,217 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writeint2(int2 value)
|
||||
{
|
||||
WriteInt32(value.x);
|
||||
WriteInt32(value.y);
|
||||
}
|
||||
|
||||
public void Writeint3(int3 value)
|
||||
{
|
||||
WriteInt32(value.x);
|
||||
WriteInt32(value.y);
|
||||
WriteInt32(value.z);
|
||||
}
|
||||
|
||||
public void Writeint4(int4 value)
|
||||
{
|
||||
WriteInt32(value.x);
|
||||
WriteInt32(value.y);
|
||||
WriteInt32(value.z);
|
||||
WriteInt32(value.w);
|
||||
}
|
||||
|
||||
public void Writeint2x2(int2x2 value)
|
||||
{
|
||||
Writeint2(value.c0);
|
||||
Writeint2(value.c1);
|
||||
}
|
||||
|
||||
public void Writeint2x3(int2x3 value)
|
||||
{
|
||||
Writeint2(value.c0);
|
||||
Writeint2(value.c1);
|
||||
Writeint2(value.c2);
|
||||
}
|
||||
|
||||
public void Writeint2x4(int2x4 value)
|
||||
{
|
||||
Writeint2(value.c0);
|
||||
Writeint2(value.c1);
|
||||
Writeint2(value.c2);
|
||||
Writeint2(value.c3);
|
||||
}
|
||||
|
||||
public void Writeint3x2(int3x2 value)
|
||||
{
|
||||
Writeint3(value.c0);
|
||||
Writeint3(value.c1);
|
||||
}
|
||||
|
||||
public void Writeint3x3(int3x3 value)
|
||||
{
|
||||
Writeint3(value.c0);
|
||||
Writeint3(value.c1);
|
||||
Writeint3(value.c2);
|
||||
}
|
||||
|
||||
public void Writeint3x4(int3x4 value)
|
||||
{
|
||||
Writeint3(value.c0);
|
||||
Writeint3(value.c1);
|
||||
Writeint3(value.c2);
|
||||
Writeint3(value.c3);
|
||||
}
|
||||
|
||||
public void Writeint4x2(int4x2 value)
|
||||
{
|
||||
Writeint4(value.c0);
|
||||
Writeint4(value.c1);
|
||||
}
|
||||
|
||||
public void Writeint4x3(int4x3 value)
|
||||
{
|
||||
Writeint4(value.c0);
|
||||
Writeint4(value.c1);
|
||||
Writeint4(value.c2);
|
||||
}
|
||||
|
||||
public void Writeint4x4(int4x4 value)
|
||||
{
|
||||
Writeint4(value.c0);
|
||||
Writeint4(value.c1);
|
||||
Writeint4(value.c2);
|
||||
Writeint4(value.c3);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public int2 Readint2()
|
||||
{
|
||||
return new int2
|
||||
{
|
||||
x = ReadInt32(),
|
||||
y = ReadInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public int3 Readint3()
|
||||
{
|
||||
return new int3()
|
||||
{
|
||||
x = ReadInt32(),
|
||||
y = ReadInt32(),
|
||||
z = ReadInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public int4 Readint4()
|
||||
{
|
||||
return new int4()
|
||||
{
|
||||
x = ReadInt32(),
|
||||
y = ReadInt32(),
|
||||
z = ReadInt32(),
|
||||
w = ReadInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public int2x2 Readint2x2()
|
||||
{
|
||||
return new int2x2()
|
||||
{
|
||||
c0 = Readint2(),
|
||||
c1 = Readint2()
|
||||
};
|
||||
}
|
||||
|
||||
public int2x3 Readint2x3()
|
||||
{
|
||||
return new int2x3()
|
||||
{
|
||||
c0 = Readint2(),
|
||||
c1 = Readint2(),
|
||||
c2 = Readint2()
|
||||
};
|
||||
}
|
||||
|
||||
public int2x4 Readint2x4()
|
||||
{
|
||||
return new int2x4()
|
||||
{
|
||||
c0 = Readint2(),
|
||||
c1 = Readint2(),
|
||||
c2 = Readint2(),
|
||||
c3 = Readint2()
|
||||
};
|
||||
}
|
||||
|
||||
public int3x2 Readint3x2()
|
||||
{
|
||||
return new int3x2()
|
||||
{
|
||||
c0 = Readint3(),
|
||||
c1 = Readint3()
|
||||
};
|
||||
}
|
||||
|
||||
public int3x3 Readint3x3()
|
||||
{
|
||||
return new int3x3()
|
||||
{
|
||||
c0 = Readint3(),
|
||||
c1 = Readint3(),
|
||||
c2 = Readint3()
|
||||
};
|
||||
}
|
||||
|
||||
public int3x4 Readint3x4()
|
||||
{
|
||||
return new int3x4()
|
||||
{
|
||||
c0 = Readint3(),
|
||||
c1 = Readint3(),
|
||||
c2 = Readint3(),
|
||||
c3 = Readint3()
|
||||
};
|
||||
}
|
||||
|
||||
public int4x2 Readint4x2()
|
||||
{
|
||||
return new int4x2()
|
||||
{
|
||||
c0 = Readint4(),
|
||||
c1 = Readint4()
|
||||
};
|
||||
}
|
||||
|
||||
public int4x3 Readint4x3()
|
||||
{
|
||||
return new int4x3()
|
||||
{
|
||||
c0 = Readint4(),
|
||||
c1 = Readint4(),
|
||||
c2 = Readint4()
|
||||
};
|
||||
}
|
||||
|
||||
public int4x4 Readint4x4()
|
||||
{
|
||||
return new int4x4()
|
||||
{
|
||||
c0 = Readint4(),
|
||||
c1 = Readint4(),
|
||||
c2 = Readint4(),
|
||||
c3 = Readint4()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74baebf467113dd4ca83dd656a4abb67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,84 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Mathematics.Geometry;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writequaternion(quaternion value)
|
||||
{
|
||||
Writefloat4(value.value);
|
||||
}
|
||||
|
||||
public void Writerandom(Unity.Mathematics.Random random)
|
||||
{
|
||||
WriteUInt32(random.state);
|
||||
}
|
||||
|
||||
public void WriteRigidTransform(RigidTransform value)
|
||||
{
|
||||
Writequaternion(value.rot);
|
||||
Writefloat3(value.pos);
|
||||
}
|
||||
#if UNITYMATHEMATICS_131
|
||||
public void WriteAffineTransform(AffineTransform value)
|
||||
{
|
||||
Writefloat3x3(value.rs);
|
||||
Writefloat3(value.t);
|
||||
}
|
||||
#endif
|
||||
#if UNITYMATHEMATICS_132
|
||||
public void ReadMinMaxAABB(MinMaxAABB minMaxAABB)
|
||||
{
|
||||
Writefloat3(minMaxAABB.Min);
|
||||
Writefloat3(minMaxAABB.Max);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public quaternion Readquaternion()
|
||||
{
|
||||
return new quaternion(Readfloat4());
|
||||
}
|
||||
|
||||
public Random Readrandom()
|
||||
{
|
||||
return new Random() { state = ReadUInt32() };
|
||||
}
|
||||
|
||||
public RigidTransform ReadRigidTransform()
|
||||
{
|
||||
return new RigidTransform()
|
||||
{
|
||||
rot = Readquaternion(),
|
||||
pos = Readfloat3(),
|
||||
};
|
||||
}
|
||||
|
||||
#if UNITYMATHEMATICS_131
|
||||
public AffineTransform ReadAffineTransform()
|
||||
{
|
||||
return new AffineTransform()
|
||||
{
|
||||
rs = Readfloat3x3(),
|
||||
t = Readfloat3(),
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#if UNITYMATHEMATICS_132
|
||||
public MinMaxAABB ReadMinMaxAABB()
|
||||
{
|
||||
return new MinMaxAABB()
|
||||
{
|
||||
Min = Readfloat3(),
|
||||
Max = Readfloat3()
|
||||
};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34d28436e6d87044fa5dcd3b0b7fd097
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs
|
||||
uploadId: 866910
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
#if UNITYMATHEMATICS
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
public partial class Writer
|
||||
{
|
||||
public void Writeuint2(uint2 value)
|
||||
{
|
||||
WriteUInt32(value.x);
|
||||
WriteUInt32(value.y);
|
||||
}
|
||||
|
||||
public void Writeuint3(uint3 value)
|
||||
{
|
||||
WriteUInt32(value.x);
|
||||
WriteUInt32(value.y);
|
||||
WriteUInt32(value.z);
|
||||
}
|
||||
|
||||
public void Writeuint4(uint4 value)
|
||||
{
|
||||
WriteUInt32(value.x);
|
||||
WriteUInt32(value.y);
|
||||
WriteUInt32(value.z);
|
||||
WriteUInt32(value.w);
|
||||
}
|
||||
|
||||
public void Writeuint2x2(uint2x2 value)
|
||||
{
|
||||
Writeuint2(value.c0);
|
||||
Writeuint2(value.c1);
|
||||
}
|
||||
|
||||
public void Writeuint2x3(uint2x3 value)
|
||||
{
|
||||
Writeuint2(value.c0);
|
||||
Writeuint2(value.c1);
|
||||
Writeuint2(value.c2);
|
||||
}
|
||||
|
||||
public void Writeuint2x4(uint2x4 value)
|
||||
{
|
||||
Writeuint2(value.c0);
|
||||
Writeuint2(value.c1);
|
||||
Writeuint2(value.c2);
|
||||
Writeuint2(value.c3);
|
||||
}
|
||||
|
||||
public void Writeuint3x2(uint3x2 value)
|
||||
{
|
||||
Writeuint3(value.c0);
|
||||
Writeuint3(value.c1);
|
||||
}
|
||||
|
||||
public void Writeuint3x3(uint3x3 value)
|
||||
{
|
||||
Writeuint3(value.c0);
|
||||
Writeuint3(value.c1);
|
||||
Writeuint3(value.c2);
|
||||
}
|
||||
|
||||
public void Writeuint3x4(uint3x4 value)
|
||||
{
|
||||
Writeuint3(value.c0);
|
||||
Writeuint3(value.c1);
|
||||
Writeuint3(value.c2);
|
||||
Writeuint3(value.c3);
|
||||
}
|
||||
|
||||
public void Writeuint4x2(uint4x2 value)
|
||||
{
|
||||
Writeuint4(value.c0);
|
||||
Writeuint4(value.c1);
|
||||
}
|
||||
|
||||
public void Writeuint4x3(uint4x3 value)
|
||||
{
|
||||
Writeuint4(value.c0);
|
||||
Writeuint4(value.c1);
|
||||
Writeuint4(value.c2);
|
||||
}
|
||||
|
||||
public void Writeuint4x4(uint4x4 value)
|
||||
{
|
||||
Writeuint4(value.c0);
|
||||
Writeuint4(value.c1);
|
||||
Writeuint4(value.c2);
|
||||
Writeuint4(value.c3);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Reader
|
||||
{
|
||||
public uint2 Readuint2()
|
||||
{
|
||||
return new uint2
|
||||
{
|
||||
x = ReadUInt32(),
|
||||
y = ReadUInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public uint3 Readuint3()
|
||||
{
|
||||
return new uint3()
|
||||
{
|
||||
x = ReadUInt32(),
|
||||
y = ReadUInt32(),
|
||||
z = ReadUInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public uint4 Readuint4()
|
||||
{
|
||||
return new uint4()
|
||||
{
|
||||
x = ReadUInt32(),
|
||||
y = ReadUInt32(),
|
||||
z = ReadUInt32(),
|
||||
w = ReadUInt32()
|
||||
};
|
||||
}
|
||||
|
||||
public uint2x2 Readuint2x2()
|
||||
{
|
||||
return new uint2x2()
|
||||
{
|
||||
c0 = Readuint2(),
|
||||
c1 = Readuint2()
|
||||
};
|
||||
}
|
||||
|
||||
public uint2x3 Readuint2x3()
|
||||
{
|
||||
return new uint2x3()
|
||||
{
|
||||
c0 = Readuint2(),
|
||||
c1 = Readuint2(),
|
||||
c2 = Readuint2()
|
||||
};
|
||||
}
|
||||
|
||||
public uint2x4 Readuint2x4()
|
||||
{
|
||||
return new uint2x4()
|
||||
{
|
||||
c0 = Readuint2(),
|
||||
c1 = Readuint2(),
|
||||
c2 = Readuint2(),
|
||||
c3 = Readuint2()
|
||||
};
|
||||
}
|
||||
|
||||
public uint3x2 Readuint3x2()
|
||||
{
|
||||
return new uint3x2()
|
||||
{
|
||||
c0 = Readuint3(),
|
||||
c1 = Readuint3()
|
||||
};
|
||||
}
|
||||
|
||||
public uint3x3 Readuint3x3()
|
||||
{
|
||||
return new uint3x3()
|
||||
{
|
||||
c0 = Readuint3(),
|
||||
c1 = Readuint3(),
|
||||
c2 = Readuint3()
|
||||
};
|
||||
}
|
||||
|
||||
public uint3x4 Readuint3x4()
|
||||
{
|
||||
return new uint3x4()
|
||||
{
|
||||
c0 = Readuint3(),
|
||||
c1 = Readuint3(),
|
||||
c2 = Readuint3(),
|
||||
c3 = Readuint3()
|
||||
};
|
||||
}
|
||||
|
||||
public uint4x2 Readuint4x2()
|
||||
{
|
||||
return new uint4x2()
|
||||
{
|
||||
c0 = Readuint4(),
|
||||
c1 = Readuint4()
|
||||
};
|
||||
}
|
||||
|
||||
public uint4x3 Readuint4x3()
|
||||
{
|
||||
return new uint4x3()
|
||||
{
|
||||
c0 = Readuint4(),
|
||||
c1 = Readuint4(),
|
||||
c2 = Readuint4()
|
||||
};
|
||||
}
|
||||
|
||||
public uint4x4 Readuint4x4()
|
||||
{
|
||||
return new uint4x4()
|
||||
{
|
||||
c0 = Readuint4(),
|
||||
c1 = Readuint4(),
|
||||
c2 = Readuint4(),
|
||||
c3 = Readuint4()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5640c6f5fc37ed3419f18024866c816c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,833 @@
|
||||
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.
|
||||
[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
|
||||
{
|
||||
NetworkManager.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
|
||||
{
|
||||
NetworkManager.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)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03955a9bd0c9a6f44886554887435672
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Writer.Delta.cs
|
||||
uploadId: 866910
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2633f927065d9d43b8a4da09240266c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/Writer.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,90 @@
|
||||
// using FishNet.CodeGenerating; // Remove on V5
|
||||
// using FishNet.Connection;
|
||||
// using FishNet.Documenting;
|
||||
// using FishNet.Object;
|
||||
// using FishNet.Serializing.Helping;
|
||||
// using FishNet.Transporting;
|
||||
// using System;
|
||||
// using System.Collections.Generic;
|
||||
// using UnityEngine;
|
||||
|
||||
// namespace FishNet.Serializing
|
||||
// {
|
||||
|
||||
// /// <summary>
|
||||
// /// Extensions to Write methods. Used by Write<T>.
|
||||
// /// </summary>
|
||||
// [APIExclude]
|
||||
// public static class WriterExtensions
|
||||
// {
|
||||
|
||||
// /// <summary>
|
||||
// /// Types which are are set to auto pack by default.
|
||||
// /// </summary>
|
||||
// internal static HashSet<System.Type> DefaultPackedTypes = new HashSet<System.Type>();
|
||||
|
||||
// static WriterExtensions()
|
||||
// {
|
||||
// DefaultPackedTypes.Add(typeof(short));
|
||||
// DefaultPackedTypes.Add(typeof(ushort));
|
||||
// DefaultPackedTypes.Add(typeof(int));
|
||||
// DefaultPackedTypes.Add(typeof(uint));
|
||||
// DefaultPackedTypes.Add(typeof(long));
|
||||
// DefaultPackedTypes.Add(typeof(ulong));
|
||||
// DefaultPackedTypes.Add(typeof(Color));
|
||||
// DefaultPackedTypes.Add(typeof(Quaternion));
|
||||
// DefaultPackedTypes.Add(typeof(Vector2Int));
|
||||
// DefaultPackedTypes.Add(typeof(Vector3Int));
|
||||
// DefaultPackedTypes.Add(typeof(Quaternion));
|
||||
// }
|
||||
|
||||
// // public static void WriteDictionary<TKey, TValue>(this Writer writer, Dictionary<TKey, TValue> dict) => writer.WriteDictionary(dict);
|
||||
// // public static void WriteByte(this Writer writer, byte value) => writer.WriteByte(value);
|
||||
// //
|
||||
// // public static void WriteBytes(this Writer writer, byte[] buffer, int offset, int count) => writer.WriteBytes(buffer, offset, count);
|
||||
// //
|
||||
// // public static void WriteBytesAndSize(this Writer writer, byte[] buffer, int offset, int count) => writer.WriteBytesAndSize(buffer, offset, count);
|
||||
// // public static void WriteBytesAndSize(this Writer writer, byte[] value) => writer.WriteBytesAndSize(value);
|
||||
|
||||
// // public static void WriteSByte(this Writer writer, sbyte value) => writer.WriteSByte(value);
|
||||
// // public static void WriteChar(this Writer writer, char value) => writer.WriteChar(value);
|
||||
// // public static void WriteBoolean(this Writer writer, bool value) => writer.WriteBoolean(value);
|
||||
// // public static void WriteUInt16(this Writer writer, ushort value) => writer.WriteUInt16(value);
|
||||
// // public static void WriteInt16(this Writer writer, short value) => writer.WriteInt16(value);
|
||||
// // public static void WriteInt32(this Writer writer, int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteInt32(value, packType);
|
||||
// // public static void WriteUInt32(this Writer writer, uint value, AutoPackType packType = AutoPackType.Packed) => writer.WriteUInt32(value, packType);
|
||||
// // public static void WriteInt64(this Writer writer, long value, AutoPackType packType = AutoPackType.Packed) => writer.WriteInt64(value, packType);
|
||||
// // public static void WriteUInt64(this Writer writer, ulong value, AutoPackType packType = AutoPackType.Packed) => writer.WriteUInt64(value, packType);
|
||||
// // public static void WriteSingle(this Writer writer, float value, AutoPackType packType = AutoPackType.Unpacked) => writer.WriteSingle(value, packType);
|
||||
// // public static void WriteDouble(this Writer writer, double value) => writer.WriteDouble(value);
|
||||
// // public static void WriteDecimal(this Writer writer, decimal value) => writer.WriteDecimal(value);
|
||||
// // public static void WriteString(this Writer writer, string value) => writer.WriteString(value);
|
||||
// // public static void WriteArraySegmentAndSize(this Writer writer, ArraySegment<byte> value) => writer.WriteArraySegmentAndSize(value);
|
||||
// //
|
||||
// // public static void WriteArraySegment(this Writer writer, ArraySegment<byte> value) => writer.WriteArraySegment(value);
|
||||
// // public static void WriteVector2(this Writer writer, Vector2 value) => writer.WriteVector2(value);
|
||||
// // public static void WriteVector3(this Writer writer, Vector3 value) => writer.WriteVector3(value);
|
||||
// // public static void WriteVector4(this Writer writer, Vector4 value) => writer.WriteVector4(value);
|
||||
// // public static void WriteVector2Int(this Writer writer, Vector2Int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteVector2Int(value, packType);
|
||||
// // public static void WriteVector3Int(this Writer writer, Vector3Int value, AutoPackType packType = AutoPackType.Packed) => writer.WriteVector3Int(value, packType);
|
||||
// // public static void WriteColor(this Writer writer, Color value, AutoPackType packType) => writer.WriteColor(value, packType);
|
||||
// // public static void WriteColor32(this Writer writer, Color32 value) => writer.WriteColor32(value);
|
||||
// // public static void WriteQuaternion(this Writer writer, Quaternion value, AutoPackType packType = AutoPackType.Packed) => writer.WriteQuaternion(value, packType);
|
||||
// // public static void WriteRect(this Writer writer, Rect value) => writer.WriteRect(value);
|
||||
// // public static void WritePlane(this Writer writer, Plane value) => writer.WritePlane(value);
|
||||
// // public static void WriteRay(this Writer writer, Ray value) => writer.WriteRay(value);
|
||||
// // public static void WriteRay2D(this Writer writer, Ray2D value) => writer.WriteRay2D(value);
|
||||
// // public static void WriteMatrix4x4(this Writer writer, Matrix4x4 value) => writer.WriteMatrix4x4(value);
|
||||
// // public static void WriteGuidAllocated(this Writer writer, System.Guid value) => writer.WriteGuidAllocated(value);
|
||||
// // public static void WriteGameObject(this Writer writer, GameObject value) => writer.WriteGameObject(value);
|
||||
// // public static void WriteTransform(this Writer writer, Transform value) => writer.WriteTransform(value);
|
||||
// // public static void WriteNetworkObject(this Writer writer, NetworkObject value) => writer.WriteNetworkObject(value);
|
||||
// // public static void WriteNetworkBehaviour(this Writer writer, NetworkBehaviour value) => writer.WriteNetworkBehaviour(value);
|
||||
// // public static void WriteChannel(this Writer writer, Channel value) => writer.WriteChannel(value);
|
||||
// // public static void WriteNetworkConnection(this Writer writer, NetworkConnection value) => writer.WriteNetworkConnection(value);
|
||||
// //
|
||||
// // public static void Write<T>(this Writer writer, T value) => writer.Write<T>(value);
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 493e880d63e9372449c0f0e63890aaef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/WriterExtensions.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,210 @@
|
||||
using FishNet.Managing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
|
||||
namespace FishNet.Serializing
|
||||
{
|
||||
/// <summary>
|
||||
/// Writer which is reused to save on garbage collection and performance.
|
||||
/// </summary>
|
||||
public sealed class PooledWriter : Writer
|
||||
{
|
||||
public void Store() => WriterPool.Store(this);
|
||||
public void StoreLength() => WriterPool.StoreLength(this);
|
||||
|
||||
[Obsolete("Use Clear instead.")]
|
||||
public void ResetState() => Clear();
|
||||
|
||||
[Obsolete("This does not function.")]
|
||||
public void InitializeState() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collection of PooledWriter. Stores and gets PooledWriter.
|
||||
/// </summary>
|
||||
public static class WriterPool
|
||||
{
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Pool of writers where length is the minimum and increased at runtime.
|
||||
/// </summary>
|
||||
private static readonly Stack<PooledWriter> _pool = new();
|
||||
/// <summary>
|
||||
/// Pool of writers where length is of minimum key and may be increased at runtime.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<int, Stack<PooledWriter>> _lengthPool = new();
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
/// <summary>
|
||||
/// Length of each bracket when using the length based writer pool.
|
||||
/// </summary>
|
||||
internal const int LENGTH_BRACKET = 1000;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets a writer from the pool.
|
||||
/// </summary>
|
||||
public static PooledWriter Retrieve(NetworkManager networkManager)
|
||||
{
|
||||
PooledWriter result;
|
||||
if (!_pool.TryPop(out result))
|
||||
result = new();
|
||||
|
||||
result.Clear(networkManager);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Gets a writer from the pool.
|
||||
/// </summary>
|
||||
public static PooledWriter Retrieve()
|
||||
{
|
||||
return Retrieve(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next writer in the pool of minimum length.
|
||||
/// </summary>
|
||||
/// <param name = "length">Minimum length the writer buffer must be.</param>
|
||||
public static PooledWriter Retrieve(int length)
|
||||
{
|
||||
return Retrieve(null, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next writer in the pool of minimum length.
|
||||
/// </summary>
|
||||
/// <param name = "length">Minimum length the writer buffer must be.</param>
|
||||
public static PooledWriter Retrieve(NetworkManager networkManager, int length)
|
||||
{
|
||||
/* The index returned will be for writers which have
|
||||
* length as a minimum capacity.
|
||||
* EG: if length is 1200 / 1000 (length_bracket) result
|
||||
* will be index 1. Index 0 will be up to 1000, while
|
||||
* index 1 will be up to 2000. */
|
||||
int index = GetDictionaryIndex(length);
|
||||
Stack<PooledWriter> stack;
|
||||
PooledWriter result;
|
||||
// There is already one pooled.
|
||||
if (_lengthPool.TryGetValue(index, out stack) && stack.TryPop(out result))
|
||||
{
|
||||
result.Clear(networkManager);
|
||||
}
|
||||
// Not pooled yet or failed to pop.
|
||||
else
|
||||
{
|
||||
// Get any ol' writer.
|
||||
result = Retrieve(networkManager);
|
||||
/* Ensure length to fill it's bracket.
|
||||
* Increase index by 1 since 0 index would
|
||||
* just return 0 as the capacity. */
|
||||
int requiredCapacity = (index + 1) * LENGTH_BRACKET;
|
||||
result.EnsureBufferCapacity(requiredCapacity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a writer to the appropriate length pool.
|
||||
/// Writers must be a minimum of 1000 bytes in length to be sorted by length.
|
||||
/// Writers which do not meet the minimum will be resized to 1000 bytes.
|
||||
/// </summary>
|
||||
public static void StoreLength(PooledWriter writer)
|
||||
{
|
||||
int index = GetDictionaryIndex(writer);
|
||||
Stack<PooledWriter> stack;
|
||||
if (!_lengthPool.TryGetValue(index, out stack))
|
||||
{
|
||||
stack = new();
|
||||
_lengthPool[index] = stack;
|
||||
}
|
||||
|
||||
stack.Push(writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a writer to the pool.
|
||||
/// </summary>
|
||||
public static void Store(PooledWriter writer)
|
||||
{
|
||||
_pool.Push(writer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts writer back into pool if not null, and nullifies source reference.
|
||||
/// </summary>
|
||||
public static void StoreAndDefault(ref PooledWriter writer)
|
||||
{
|
||||
if (writer != null)
|
||||
{
|
||||
_pool.Push(writer);
|
||||
writer = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Dictionary indexes.
|
||||
/// <summary>
|
||||
/// Gets which index to use for length when retrieving a writer.
|
||||
/// </summary>
|
||||
private static int GetDictionaryIndex(int length)
|
||||
{
|
||||
/* The index returned will be for writers which have
|
||||
* length as a minimum capacity.
|
||||
* EG: if length is 1200 / 1000 (length_bracket) result
|
||||
* will be index 1. Index 0 will be up to 1000, while
|
||||
* index 1 will be up to 2000. So to accomodate 1200
|
||||
* length index 1 must be used as 0 has a maximum of 1000. */
|
||||
|
||||
/* Examples if length_bracket is 1000, using floor:
|
||||
* 800 / 1000 = 0.
|
||||
* 1200 / 1000 = 1.
|
||||
* 1000 / 1000 = 1. But has 0 remainder so is reduced by 1, resulting in 0.
|
||||
*/
|
||||
int index = UnityEngine.Mathf.FloorToInt(length / LENGTH_BRACKET);
|
||||
if (index > 0 && length % LENGTH_BRACKET == 0)
|
||||
index--;
|
||||
|
||||
// UnityEngine.Debug.Log($"Returning length {length} from index {index}");
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets which index to use for length when storing a writer.
|
||||
/// </summary>
|
||||
private static int GetDictionaryIndex(PooledWriter writer)
|
||||
{
|
||||
int capacity = writer.Capacity;
|
||||
/* If capacity is less than 1000 then the writer
|
||||
* does not meet the minimum length bracket. This should never
|
||||
* be the case unless the user perhaps manually calls this method. */
|
||||
if (capacity < LENGTH_BRACKET)
|
||||
{
|
||||
capacity = LENGTH_BRACKET;
|
||||
writer.EnsureBufferCapacity(LENGTH_BRACKET);
|
||||
}
|
||||
|
||||
/* Since capacity is set to minimum of length_bracket
|
||||
* capacity / length_bracket will always be at least 1.
|
||||
*
|
||||
* Here are some result examples using floor:
|
||||
* 1000 / 1000 = 1.
|
||||
* 1200 / 1000 = 1.
|
||||
* 2400 / 1000 = 2.
|
||||
*/
|
||||
int index = UnityEngine.Mathf.FloorToInt(capacity / LENGTH_BRACKET);
|
||||
/* As mentioned the index will always be a minimum of 1. Because of this
|
||||
* we can safely reduce index by 1 and it not be negative.
|
||||
* This reduction also ensures the writer ends up in the proper pool.
|
||||
* Since index 0 ensures minimum of 1000, 1000-1999 would go there.
|
||||
* Just as 2000-2999 would go into 1. */
|
||||
index--;
|
||||
|
||||
// UnityEngine.Debug.Log($"Storing capacity {capacity} at index {index}");
|
||||
return index;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99b13ccd24eb7264abf67780ef86e44a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/WriterPool.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,2 @@
|
||||
// Remove on V5
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 264951af6d87de34e8809b550a7956b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Serializing/WriterStatics.cs
|
||||
uploadId: 866910
|
||||
Reference in New Issue
Block a user