com.unity.netcode.gameobjects@1.0.0-pre.8

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.0.0-pre.8] - 2022-04-27

### Changed

- `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901)

### Removed
- Removed `SIPTransport` (#1870)

- Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs).

### Fixed

- Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890)
- Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884)
- Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883)
- Fixed an issue where UNetTransport.StartServer would return success even if the underlying transport failed to start (#854)
- Passing generic types to RPCs no longer causes a native crash (#1901)
- Fixed an issue where calling `Shutdown` on a `NetworkManager` that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877)
This commit is contained in:
Unity Technologies
2022-04-27 00:00:00 +00:00
parent 60e2dabef4
commit add668dfd2
119 changed files with 4434 additions and 1801 deletions

View File

@@ -1,13 +1,16 @@
using System;
using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// Two-way serializer wrapping FastBufferReader or FastBufferWriter.
///
///
/// Implemented as a ref struct for two reasons:
/// 1. The BufferSerializer cannot outlive the FBR/FBW it wraps or using it will cause a crash
/// 2. The BufferSerializer must always be passed by reference and can't be copied
///
/// Ref structs help enforce both of those rules: they can't out live the stack context in which they were
/// Ref structs help enforce both of those rules: they can't ref live the stack context in which they were
/// created, and they're always passed by reference no matter what.
///
/// BufferSerializer doesn't wrapp FastBufferReader or FastBufferWriter directly because it can't.
@@ -58,168 +61,63 @@ namespace Unity.Netcode
return m_Implementation.GetFastBufferWriter();
}
/// <summary>
/// Serialize an INetworkSerializable
///
/// Throws OverflowException if the end of the buffer has been reached.
/// Write buffers will grow up to the maximum allowable message size before throwing OverflowException.
/// </summary>
/// <param name="value">Value to serialize</param>
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new()
{
m_Implementation.SerializeNetworkSerializable(ref value);
}
public void SerializeValue(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValue(ref s, oneByteChars);
public void SerializeValue(ref byte value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector2 value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector2[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector3 value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector3[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector4 value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Vector4[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Quaternion value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Quaternion[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Color value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Color[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Color32 value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Color32[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Ray value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Ray[] value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Ray2D value) => m_Implementation.SerializeValue(ref value);
public void SerializeValue(ref Ray2D[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Serialize a string.
///
/// Note: Will ALWAYS allocate a new string when reading.
///
/// Throws OverflowException if the end of the buffer has been reached.
/// Write buffers will grow up to the maximum allowable message size before throwing OverflowException.
/// </summary>
/// <param name="s">Value to serialize</param>
/// <param name="oneByteChars">
/// If true, will truncate each char to one byte.
/// This is slower than two-byte chars, but uses less bandwidth.
/// </param>
public void SerializeValue(ref string s, bool oneByteChars = false)
{
m_Implementation.SerializeValue(ref s, oneByteChars);
}
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new() => m_Implementation.SerializeNetworkSerializable(ref value);
/// <summary>
/// Serialize an array value.
///
/// Note: Will ALWAYS allocate a new array when reading.
/// If you have a statically-sized array that you know is large enough, it's recommended to
/// serialize the size yourself and iterate serializing array members.
///
/// (This is because C# doesn't allow setting an array's length value, so deserializing
/// into an existing array of larger size would result in an array that doesn't have as many values
/// as its Length indicates it should.)
///
/// Throws OverflowException if the end of the buffer has been reached.
/// Write buffers will grow up to the maximum allowable message size before throwing OverflowException.
/// </summary>
/// <param name="array">Value to serialize</param>
public void SerializeValue<T>(ref T[] array) where T : unmanaged
{
m_Implementation.SerializeValue(ref array);
}
/// <summary>
/// Serialize a single byte
///
/// Throws OverflowException if the end of the buffer has been reached.
/// Write buffers will grow up to the maximum allowable message size before throwing OverflowException.
/// </summary>
/// <param name="value">Value to serialize</param>
public void SerializeValue(ref byte value)
{
m_Implementation.SerializeValue(ref value);
}
/// <summary>
/// Serialize an unmanaged type. Supports basic value types as well as structs.
/// The provided type will be copied to/from the buffer as it exists in memory.
///
/// Throws OverflowException if the end of the buffer has been reached.
/// Write buffers will grow up to the maximum allowable message size before throwing OverflowException.
/// </summary>
/// <param name="value">Value to serialize</param>
public void SerializeValue<T>(ref T value) where T : unmanaged
{
m_Implementation.SerializeValue(ref value);
}
/// <summary>
/// Allows faster serialization by batching bounds checking.
/// When you know you will be writing multiple fields back-to-back and you know the total size,
/// you can call PreCheck() once on the total size, and then follow it with calls to
/// SerializeValuePreChecked() for faster serialization. Write buffers will grow during PreCheck()
/// if needed.
///
/// PreChecked serialization operations will throw OverflowException in editor and development builds if you
/// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown
/// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following
/// operations in release builds.
///
/// To get the correct size to check for, use FastBufferWriter.GetWriteSize(value) or
/// FastBufferWriter.GetWriteSize&lt;type&gt;()
/// </summary>
/// <param name="amount">Number of bytes you plan to read or write</param>
/// <returns>True if the read/write can proceed, false otherwise.</returns>
public bool PreCheck(int amount)
{
return m_Implementation.PreCheck(amount);
}
/// <summary>
/// Serialize a string.
///
/// Note: Will ALWAYS allocate a new string when reading.
///
/// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only
/// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple
/// serialization operations in one function call instead of having to do bounds checking on every call.
/// </summary>
/// <param name="s">Value to serialize</param>
/// <param name="oneByteChars">
/// If true, will truncate each char to one byte.
/// This is slower than two-byte chars, but uses less bandwidth.
/// </param>
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false)
{
m_Implementation.SerializeValuePreChecked(ref s, oneByteChars);
}
/// <summary>
/// Serialize an array value.
///
/// Note: Will ALWAYS allocate a new array when reading.
/// If you have a statically-sized array that you know is large enough, it's recommended to
/// serialize the size yourself and iterate serializing array members.
///
/// (This is because C# doesn't allow setting an array's length value, so deserializing
/// into an existing array of larger size would result in an array that doesn't have as many values
/// as its Length indicates it should.)
///
/// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only
/// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple
/// serialization operations in one function call instead of having to do bounds checking on every call.
/// </summary>
/// <param name="array">Value to serialize</param>
public void SerializeValuePreChecked<T>(ref T[] array) where T : unmanaged
{
m_Implementation.SerializeValuePreChecked(ref array);
}
/// <summary>
/// Serialize a single byte
///
/// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only
/// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple
/// serialization operations in one function call instead of having to do bounds checking on every call.
/// </summary>
/// <param name="value">Value to serialize</param>
public void SerializeValuePreChecked(ref byte value)
{
m_Implementation.SerializeValuePreChecked(ref value);
}
/// <summary>
/// Serialize an unmanaged type. Supports basic value types as well as structs.
/// The provided type will be copied to/from the buffer as it exists in memory.
///
/// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only
/// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple
/// serialization operations in one function call instead of having to do bounds checking on every call.
/// </summary>
/// <param name="value">Value to serialize</param>
public void SerializeValuePreChecked<T>(ref T value) where T : unmanaged
{
m_Implementation.SerializeValuePreChecked(ref value);
}
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValuePreChecked(ref s, oneByteChars);
public void SerializeValuePreChecked(ref byte value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector2 value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector2[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector3 value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector3[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Vector4[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Quaternion value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Color value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Color[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Color32 value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Color32[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Ray value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Ray[] value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Ray2D value) => m_Implementation.SerializeValuePreChecked(ref value);
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Implementation.SerializeValuePreChecked(ref value);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using UnityEngine;
namespace Unity.Netcode
{
@@ -24,54 +25,63 @@ namespace Unity.Netcode
throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false");
}
public void SerializeValue(ref string s, bool oneByteChars = false)
{
m_Reader.ReadValueSafe(out s, oneByteChars);
}
public void SerializeValue(ref string s, bool oneByteChars = false) => m_Reader.ReadValueSafe(out s, oneByteChars);
public void SerializeValue(ref byte value) => m_Reader.ReadByteSafe(out value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadValue(out value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Reader.ReadValue(out value);
public void SerializeValue(ref Vector2 value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Vector2[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Vector3 value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Vector3[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Vector4 value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Vector4[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Quaternion value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Quaternion[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Color value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Color[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Color32 value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Color32[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Ray value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Ray[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Ray2D value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue(ref Ray2D[] value) => m_Reader.ReadValueSafe(out value);
public void SerializeValue<T>(ref T[] array) where T : unmanaged
{
m_Reader.ReadValueSafe(out array);
}
public void SerializeValue(ref byte value)
{
m_Reader.ReadByteSafe(out value);
}
public void SerializeValue<T>(ref T value) where T : unmanaged
{
m_Reader.ReadValueSafe(out value);
}
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new()
{
m_Reader.ReadNetworkSerializable(out value);
}
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new() => m_Reader.ReadNetworkSerializable(out value);
public bool PreCheck(int amount)
{
return m_Reader.TryBeginRead(amount);
}
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false)
{
m_Reader.ReadValue(out s, oneByteChars);
}
public void SerializeValuePreChecked<T>(ref T[] array) where T : unmanaged
{
m_Reader.ReadValue(out array);
}
public void SerializeValuePreChecked(ref byte value)
{
m_Reader.ReadValue(out value);
}
public void SerializeValuePreChecked<T>(ref T value) where T : unmanaged
{
m_Reader.ReadValue(out value);
}
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Reader.ReadValue(out s, oneByteChars);
public void SerializeValuePreChecked(ref byte value) => m_Reader.ReadByte(out value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector2 value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector2[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector3 value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector3[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector4 value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Vector4[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Quaternion value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Color value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Color[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Color32 value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Color32[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Ray value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Ray[] value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Ray2D value) => m_Reader.ReadValue(out value);
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Reader.ReadValue(out value);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using UnityEngine;
namespace Unity.Netcode
{
@@ -24,25 +25,32 @@ namespace Unity.Netcode
return m_Writer;
}
public void SerializeValue(ref string s, bool oneByteChars = false)
{
m_Writer.WriteValueSafe(s, oneByteChars);
}
public void SerializeValue<T>(ref T[] array) where T : unmanaged
{
m_Writer.WriteValueSafe(array);
}
public void SerializeValue(ref byte value)
{
m_Writer.WriteByteSafe(value);
}
public void SerializeValue<T>(ref T value) where T : unmanaged
{
m_Writer.WriteValueSafe(value);
}
public void SerializeValue(ref string s, bool oneByteChars = false) => m_Writer.WriteValueSafe(s, oneByteChars);
public void SerializeValue(ref byte value) => m_Writer.WriteByteSafe(value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValueSafe(value);
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Writer.WriteValue(value);
public void SerializeValue(ref Vector2 value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Vector2[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Vector3 value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Vector3[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Vector4 value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Vector4[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Quaternion value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Quaternion[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Color value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Color[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Color32 value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Color32[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Ray value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Ray[] value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Ray2D value) => m_Writer.WriteValueSafe(value);
public void SerializeValue(ref Ray2D[] value) => m_Writer.WriteValueSafe(value);
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new()
{
@@ -54,24 +62,30 @@ namespace Unity.Netcode
return m_Writer.TryBeginWrite(amount);
}
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false)
{
m_Writer.WriteValue(s, oneByteChars);
}
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Writer.WriteValue(s, oneByteChars);
public void SerializeValuePreChecked(ref byte value) => m_Writer.WriteByte(value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValue(value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => m_Writer.WriteValue(value);
public void SerializeValuePreChecked<T>(ref T[] array) where T : unmanaged
{
m_Writer.WriteValue(array);
}
public void SerializeValuePreChecked(ref byte value)
{
m_Writer.WriteByte(value);
}
public void SerializeValuePreChecked<T>(ref T value) where T : unmanaged
{
m_Writer.WriteValue(value);
}
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Writer.WriteValue(value);
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector2 value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector2[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector3 value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector3[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector4 value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Vector4[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Quaternion value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Color value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Color[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Color32 value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Color32[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Ray value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Ray[] value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Ray2D value) => m_Writer.WriteValue(value);
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Writer.WriteValue(value);
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
namespace Unity.Netcode
{
@@ -195,6 +196,30 @@ namespace Unity.Netcode
Handle = CreateHandle(writer.GetUnsafePtr(), length == -1 ? writer.Length : length, offset, allocator);
}
/// <summary>
/// Create a FastBufferReader from another existing FastBufferReader. This is typically used when you
/// want to change the allocator that a reader is allocated to - for example, upgrading a Temp reader to
/// a Persistent one to be processed later.
///
/// A new buffer will be created using the given allocator and the value will be copied in.
/// FastBufferReader will then own the data.
///
/// The exception to this is when the allocator passed in is Allocator.None. In this scenario,
/// ownership of the data remains with the caller and the reader will point at it directly.
/// When created with Allocator.None, FastBufferReader will allocate some internal data using
/// Allocator.Temp, so it should be treated as if it's a ref struct and not allowed to outlive
/// the context in which it was created (it should neither be returned from that function nor
/// stored anywhere in heap memory).
/// </summary>
/// <param name="reader">The reader to copy from</param>
/// <param name="allocator">The allocator to use</param>
/// <param name="length">The number of bytes to copy (all if this is -1)</param>
/// <param name="offset">The offset of the buffer to start copying from</param>
public unsafe FastBufferReader(FastBufferReader reader, Allocator allocator, int length = -1, int offset = 0)
{
Handle = CreateHandle(reader.GetUnsafePtr(), length == -1 ? reader.Length : length, offset, allocator);
}
/// <summary>
/// Frees the allocated buffer
/// </summary>
@@ -492,61 +517,6 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Writes an unmanaged array
/// NOTE: ALLOCATES
/// </summary>
/// <param name="array">Stores the read array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValue<T>(out T[] array) where T : unmanaged
{
ReadValue(out int sizeInTs);
int sizeInBytes = sizeInTs * sizeof(T);
array = new T[sizeInTs];
fixed (T* native = array)
{
byte* bytes = (byte*)(native);
ReadBytes(bytes, sizeInBytes);
}
}
/// <summary>
/// Reads an unmanaged array
/// NOTE: ALLOCATES
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="array">Stores the read array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValueSafe<T>(out T[] array) where T : unmanaged
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferReader in bytewise mode while in a bitwise context.");
}
#endif
if (!TryBeginReadInternal(sizeof(int)))
{
throw new OverflowException("Reading past the end of the buffer");
}
ReadValue(out int sizeInTs);
int sizeInBytes = sizeInTs * sizeof(T);
if (!TryBeginReadInternal(sizeInBytes))
{
throw new OverflowException("Reading past the end of the buffer");
}
array = new T[sizeInTs];
fixed (T* native = array)
{
byte* bytes = (byte*)(native);
ReadBytes(bytes, sizeInBytes);
}
}
/// <summary>
/// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it.
/// </summary>
@@ -711,69 +681,155 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Read a value of any unmanaged type to the buffer.
/// It will be copied from the buffer exactly as it existed in memory on the writing end.
/// </summary>
/// <param name="value">The read value</param>
/// <typeparam name="T">Any unmanaged type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValue<T>(out T value) where T : unmanaged
private unsafe void ReadUnmanaged<T>(out T value) where T : unmanaged
{
int len = sizeof(T);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferReader in bytewise mode while in a bitwise context.");
}
if (Handle->Position + len > Handle->AllowedReadMark)
{
throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()");
}
#endif
fixed (T* ptr = &value)
{
UnsafeUtility.MemCpy((byte*)ptr, Handle->BufferPointer + Handle->Position, len);
byte* bytes = (byte*)ptr;
ReadBytes(bytes, sizeof(T));
}
Handle->Position += len;
}
/// <summary>
/// Read a value of any unmanaged type to the buffer.
/// It will be copied from the buffer exactly as it existed in memory on the writing end.
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">The read value</param>
/// <typeparam name="T">Any unmanaged type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValueSafe<T>(out T value) where T : unmanaged
private unsafe void ReadUnmanagedSafe<T>(out T value) where T : unmanaged
{
int len = sizeof(T);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferReader in bytewise mode while in a bitwise context.");
}
#endif
if (!TryBeginReadInternal(len))
{
throw new OverflowException("Reading past the end of the buffer");
}
fixed (T* ptr = &value)
{
UnsafeUtility.MemCpy((byte*)ptr, Handle->BufferPointer + Handle->Position, len);
byte* bytes = (byte*)ptr;
ReadBytesSafe(bytes, sizeof(T));
}
Handle->Position += len;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void ReadUnmanaged<T>(out T[] value) where T : unmanaged
{
ReadUnmanaged(out int sizeInTs);
int sizeInBytes = sizeInTs * sizeof(T);
value = new T[sizeInTs];
fixed (T* ptr = value)
{
byte* bytes = (byte*)ptr;
ReadBytes(bytes, sizeInBytes);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void ReadUnmanagedSafe<T>(out T[] value) where T : unmanaged
{
ReadUnmanagedSafe(out int sizeInTs);
int sizeInBytes = sizeInTs * sizeof(T);
value = new T[sizeInTs];
fixed (T* ptr = value)
{
byte* bytes = (byte*)ptr;
ReadBytesSafe(bytes, sizeInBytes);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2 value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3 value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector4 value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector4[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Quaternion value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Quaternion[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color32 value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color32[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray2D value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray2D[] value) => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2 value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3 value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector4 value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector4[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Quaternion value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Quaternion[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color32 value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color32[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray[] value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray2D value) => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray2D[] value) => ReadUnmanagedSafe(out value);
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Runtime.CompilerServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
namespace Unity.Netcode
{
@@ -528,60 +529,6 @@ namespace Unity.Netcode
return sizeof(int) + sizeInBytes;
}
/// <summary>
/// Writes an unmanaged array
/// </summary>
/// <param name="array">The array to write</param>
/// <param name="count">The amount of elements to write</param>
/// <param name="offset">Where in the array to start</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteValue<T>(T[] array, int count = -1, int offset = 0) where T : unmanaged
{
int sizeInTs = count != -1 ? count : array.Length - offset;
int sizeInBytes = sizeInTs * sizeof(T);
WriteValue(sizeInTs);
fixed (T* native = array)
{
byte* bytes = (byte*)(native + offset);
WriteBytes(bytes, sizeInBytes);
}
}
/// <summary>
/// Writes an unmanaged array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="array">The array to write</param>
/// <param name="count">The amount of elements to write</param>
/// <param name="offset">Where in the array to start</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteValueSafe<T>(T[] array, int count = -1, int offset = 0) where T : unmanaged
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferWriter in bytewise mode while in a bitwise context.");
}
#endif
int sizeInTs = count != -1 ? count : array.Length - offset;
int sizeInBytes = sizeInTs * sizeof(T);
if (!TryBeginWriteInternal(sizeInBytes + sizeof(int)))
{
throw new OverflowException("Writing past the end of the buffer");
}
WriteValue(sizeInTs);
fixed (T* native = array)
{
byte* bytes = (byte*)(native + offset);
WriteBytes(bytes, sizeInBytes);
}
}
/// <summary>
/// Write a partial value. The specified number of bytes is written from the value and the rest is ignored.
/// </summary>
@@ -790,68 +737,170 @@ namespace Unity.Netcode
return sizeof(T);
}
/// <summary>
/// Write a value of any unmanaged type (including unmanaged structs) to the buffer.
/// It will be copied into the buffer exactly as it exists in memory.
/// </summary>
/// <param name="value">The value to copy</param>
/// <typeparam name="T">Any unmanaged type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteValue<T>(in T value) where T : unmanaged
private unsafe void WriteUnmanaged<T>(in T value) where T : unmanaged
{
int len = sizeof(T);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferWriter in bytewise mode while in a bitwise context.");
}
if (Handle->Position + len > Handle->AllowedWriteMark)
{
throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()");
}
#endif
fixed (T* ptr = &value)
{
UnsafeUtility.MemCpy(Handle->BufferPointer + Handle->Position, (byte*)ptr, len);
byte* bytes = (byte*)ptr;
WriteBytes(bytes, sizeof(T));
}
Handle->Position += len;
}
/// <summary>
/// Write a value of any unmanaged type (including unmanaged structs) to the buffer.
/// It will be copied into the buffer exactly as it exists in memory.
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">The value to copy</param>
/// <typeparam name="T">Any unmanaged type</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteValueSafe<T>(in T value) where T : unmanaged
private unsafe void WriteUnmanagedSafe<T>(in T value) where T : unmanaged
{
int len = sizeof(T);
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (Handle->InBitwiseContext)
{
throw new InvalidOperationException(
"Cannot use BufferWriter in bytewise mode while in a bitwise context.");
}
#endif
if (!TryBeginWriteInternal(len))
{
throw new OverflowException("Writing past the end of the buffer");
}
fixed (T* ptr = &value)
{
UnsafeUtility.MemCpy(Handle->BufferPointer + Handle->Position, (byte*)ptr, len);
byte* bytes = (byte*)ptr;
WriteBytesSafe(bytes, sizeof(T));
}
Handle->Position += len;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void WriteUnmanaged<T>(T[] value) where T : unmanaged
{
WriteUnmanaged(value.Length);
fixed (T* ptr = value)
{
byte* bytes = (byte*)ptr;
WriteBytes(bytes, sizeof(T) * value.Length);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe void WriteUnmanagedSafe<T>(T[] value) where T : unmanaged
{
WriteUnmanagedSafe(value.Length);
fixed (T* ptr = value)
{
byte* bytes = (byte*)ptr;
WriteBytesSafe(bytes, sizeof(T) * value.Length);
}
}
// These structs enable overloading of WriteValue with different generic constraints.
// The compiler's actually able to distinguish between overloads based on generic constraints.
// But at the bytecode level, the constraints aren't included in the method signature.
// By adding a second parameter with a defaulted value, the signatures of each generic are different,
// thus allowing overloads of methods based on the first parameter meeting constraints.
public struct ForPrimitives
{
}
public struct ForEnums
{
}
public struct ForStructs
{
}
public struct ForNetworkSerializable
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector2 value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector2[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector3 value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector3[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector4 value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector4[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Quaternion value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Quaternion[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Color value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Color[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Color32 value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Color32[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Ray value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Ray[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Ray2D value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Ray2D[] value) => WriteUnmanaged(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector2 value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector2[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector3 value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector3[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector4 value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector4[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Quaternion value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Quaternion[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Color value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Color[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Color32 value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Color32[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Ray value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Ray[] value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Ray2D value) => WriteUnmanagedSafe(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Ray2D[] value) => WriteUnmanagedSafe(value);
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace Unity.Netcode
{
/// <summary>
/// This is a wrapper that adds `INetworkSerializeByMemcpy` support to existing structs that the developer
/// doesn't have the ability to modify (for example, external structs like `Guid`).
/// </summary>
/// <typeparam name="T"></typeparam>
public struct ForceNetworkSerializeByMemcpy<T> : INetworkSerializeByMemcpy, IEquatable<ForceNetworkSerializeByMemcpy<T>> where T : unmanaged, IEquatable<T>
{
public T Value;
public ForceNetworkSerializeByMemcpy(T value)
{
Value = value;
}
public static implicit operator T(ForceNetworkSerializeByMemcpy<T> container) => container.Value;
public static implicit operator ForceNetworkSerializeByMemcpy<T>(T underlyingValue) => new ForceNetworkSerializeByMemcpy<T> { Value = underlyingValue };
public bool Equals(ForceNetworkSerializeByMemcpy<T> other)
{
return Value.Equals(other.Value);
}
public override bool Equals(object obj)
{
return obj is ForceNetworkSerializeByMemcpy<T> other && Equals(other);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d56016695cd44430a345671f7d56b18e
timeCreated: 1647635768

View File

@@ -0,0 +1,15 @@
namespace Unity.Netcode
{
/// <summary>
/// This interface is a "tag" that can be applied to a struct to mark that struct as being serializable
/// by memcpy. It's up to the developer of the struct to analyze the struct's contents and ensure it
/// is actually serializable by memcpy. This requires all of the members of the struct to be
/// `unmanaged` Plain-Old-Data values - if your struct contains a pointer (or a type that contains a pointer,
/// like `NativeList<T>`), it should be serialized via `INetworkSerializable` or via
/// `FastBufferReader`/`FastBufferWriter` extension methods.
/// </summary>
public interface INetworkSerializeByMemcpy
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 11b763f46b18465cbffb1972d737a83e
timeCreated: 1647635592

View File

@@ -1,3 +1,6 @@
using System;
using UnityEngine;
namespace Unity.Netcode
{
public interface IReaderWriter
@@ -9,17 +12,60 @@ namespace Unity.Netcode
FastBufferWriter GetFastBufferWriter();
void SerializeValue(ref string s, bool oneByteChars = false);
void SerializeValue<T>(ref T[] array) where T : unmanaged;
void SerializeValue(ref byte value);
void SerializeValue<T>(ref T value) where T : unmanaged;
void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
void SerializeValue(ref Vector2 value);
void SerializeValue(ref Vector2[] value);
void SerializeValue(ref Vector3 value);
void SerializeValue(ref Vector3[] value);
void SerializeValue(ref Vector4 value);
void SerializeValue(ref Vector4[] value);
void SerializeValue(ref Quaternion value);
void SerializeValue(ref Quaternion[] value);
void SerializeValue(ref Color value);
void SerializeValue(ref Color[] value);
void SerializeValue(ref Color32 value);
void SerializeValue(ref Color32[] value);
void SerializeValue(ref Ray value);
void SerializeValue(ref Ray[] value);
void SerializeValue(ref Ray2D value);
void SerializeValue(ref Ray2D[] value);
// Has to have a different name to avoid conflicting with "where T: unmananged"
void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new();
bool PreCheck(int amount);
void SerializeValuePreChecked(ref string s, bool oneByteChars = false);
void SerializeValuePreChecked<T>(ref T[] array) where T : unmanaged;
void SerializeValuePreChecked(ref byte value);
void SerializeValuePreChecked<T>(ref T value) where T : unmanaged;
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
void SerializeValuePreChecked(ref Vector2 value);
void SerializeValuePreChecked(ref Vector2[] value);
void SerializeValuePreChecked(ref Vector3 value);
void SerializeValuePreChecked(ref Vector3[] value);
void SerializeValuePreChecked(ref Vector4 value);
void SerializeValuePreChecked(ref Vector4[] value);
void SerializeValuePreChecked(ref Quaternion value);
void SerializeValuePreChecked(ref Quaternion[] value);
void SerializeValuePreChecked(ref Color value);
void SerializeValuePreChecked(ref Color[] value);
void SerializeValuePreChecked(ref Color32 value);
void SerializeValuePreChecked(ref Color32[] value);
void SerializeValuePreChecked(ref Ray value);
void SerializeValuePreChecked(ref Ray[] value);
void SerializeValuePreChecked(ref Ray2D value);
void SerializeValuePreChecked(ref Ray2D[] value);
}
}