1 Commits

Author SHA1 Message Date
Unity Technologies
18ffd5fdc8 com.unity.netcode.gameobjects@1.0.0
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] - 2022-06-27

### Changed

- Changed version to 1.0.0. (#2046)
2022-06-27 00:00:00 +00:00
44 changed files with 2667 additions and 67 deletions

View File

@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
## [1.0.0] - 2022-06-27
### Changed
- Changed version to 1.0.0. (#2046)
## [1.0.0-pre.10] - 2022-06-21
### Added
@@ -18,7 +24,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Changed
- Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025)
- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972)
- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now an `Action<>` taking a `ConnectionApprovalRequest` and a `ConnectionApprovalResponse` that the client code must fill (#1972) (#2002)
### Removed

View File

@@ -8,6 +8,7 @@ namespace Unity.Netcode
/// Solves for incoming values that are jittered
/// Partially solves for message loss. Unclamped lerping helps hide this, but not completely
/// </summary>
/// <typeparam name="T">The type of interpolated value</typeparam>
public abstract class BufferedLinearInterpolator<T> where T : struct
{
internal float MaxInterpolationBound = 3.0f;
@@ -24,7 +25,7 @@ namespace Unity.Netcode
}
/// <summary>
/// Theres two factors affecting interpolation: buffering (set in NetworkManagers NetworkTimeSystem) and interpolation time, which is the amount of time itll take to reach the target. This is to affect the second one.
/// There's two factors affecting interpolation: buffering (set in NetworkManager's NetworkTimeSystem) and interpolation time, which is the amount of time it'll take to reach the target. This is to affect the second one.
/// </summary>
public float MaximumInterpolationTime = 0.1f;
@@ -73,7 +74,7 @@ namespace Unity.Netcode
private bool InvalidState => m_Buffer.Count == 0 && m_LifetimeConsumedCount == 0;
/// <summary>
/// Resets Interpolator to initial state
/// Resets interpolator to initial state
/// </summary>
public void Clear()
{
@@ -85,6 +86,8 @@ namespace Unity.Netcode
/// <summary>
/// Teleports current interpolation value to targetValue.
/// </summary>
/// <param name="targetValue">The target value to teleport instantly</param>
/// <param name="serverTime">The current server time</param>
public void ResetTo(T targetValue, double serverTime)
{
m_LifetimeConsumedCount = 1;
@@ -159,6 +162,7 @@ namespace Unity.Netcode
/// </summary>
/// <param name="deltaTime">time since call</param>
/// <param name="serverTime">current server time</param>
/// <returns>The newly interpolated value of type 'T'</returns>
public T Update(float deltaTime, NetworkTime serverTime)
{
return Update(deltaTime, serverTime.TimeTicksAgo(1).Time, serverTime.Time);
@@ -170,6 +174,7 @@ namespace Unity.Netcode
/// <param name="deltaTime">time since last call</param>
/// <param name="renderTime">our current time</param>
/// <param name="serverTime">current server time</param>
/// <returns>The newly interpolated value of type 'T'</returns>
public T Update(float deltaTime, double renderTime, double serverTime)
{
TryConsumeFromBuffer(renderTime, serverTime);
@@ -222,6 +227,8 @@ namespace Unity.Netcode
/// <summary>
/// Add measurements to be used during interpolation. These will be buffered before being made available to be displayed as "latest value".
/// </summary>
/// <param name="newMeasurement">The new measurement value to use</param>
/// <param name="sentTime">The time to record for measurement</param>
public void AddMeasurement(T newMeasurement, double sentTime)
{
m_NbItemsReceivedThisFrame++;
@@ -251,6 +258,7 @@ namespace Unity.Netcode
/// <summary>
/// Gets latest value from the interpolator. This is updated every update as time goes by.
/// </summary>
/// <returns>The current interpolated value of type 'T'</returns>
public T GetInterpolatedValue()
{
return m_CurrentInterpValue;
@@ -259,33 +267,54 @@ namespace Unity.Netcode
/// <summary>
/// Method to override and adapted to the generic type. This assumes interpolation for that value will be clamped.
/// </summary>
/// <param name="start">The start value (min)</param>
/// <param name="end">The end value (max)</param>
/// <param name="time">The time value used to interpolate between start and end values (pos)</param>
/// <returns>The interpolated value</returns>
protected abstract T Interpolate(T start, T end, float time);
/// <summary>
/// Method to override and adapted to the generic type. This assumes interpolation for that value will not be clamped.
/// </summary>
/// <param name="start">The start value (min)</param>
/// <param name="end">The end value (max)</param>
/// <param name="time">The time value used to interpolate between start and end values (pos)</param>
/// <returns>The interpolated value</returns>
protected abstract T InterpolateUnclamped(T start, T end, float time);
}
/// <inheritdoc />
/// <remarks>
/// This is a buffered linear interpolator for a <see cref="float"/> type value
/// </remarks>
public class BufferedLinearInterpolatorFloat : BufferedLinearInterpolator<float>
{
/// <inheritdoc />
protected override float InterpolateUnclamped(float start, float end, float time)
{
return Mathf.LerpUnclamped(start, end, time);
}
/// <inheritdoc />
protected override float Interpolate(float start, float end, float time)
{
return Mathf.Lerp(start, end, time);
}
}
/// <inheritdoc />
/// <remarks>
/// This is a buffered linear interpolator for a <see cref="Quaternion"/> type value
/// </remarks>
public class BufferedLinearInterpolatorQuaternion : BufferedLinearInterpolator<Quaternion>
{
/// <inheritdoc />
protected override Quaternion InterpolateUnclamped(Quaternion start, Quaternion end, float time)
{
return Quaternion.SlerpUnclamped(start, end, time);
}
/// <inheritdoc />
protected override Quaternion Interpolate(Quaternion start, Quaternion end, float time)
{
return Quaternion.SlerpUnclamped(start, end, time);

View File

@@ -37,6 +37,7 @@ namespace Unity.Netcode.Components
m_SendTriggerUpdates.Clear();
}
/// <inheritdoc />
public void NetworkUpdate(NetworkUpdateStage updateStage)
{
switch (updateStage)

View File

@@ -5,20 +5,46 @@ using UnityEngine;
namespace Unity.Netcode.Components
{
/// <summary>
/// A component for syncing transforms
/// A component for syncing transforms.
/// NetworkTransform will read the underlying transform and replicate it to clients.
/// The replicated value will be automatically be interpolated (if active) and applied to the underlying GameObject's transform
/// The replicated value will be automatically be interpolated (if active) and applied to the underlying GameObject's transform.
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Netcode/" + nameof(NetworkTransform))]
[DefaultExecutionOrder(100000)] // this is needed to catch the update time after the transform was updated by user scripts
public class NetworkTransform : NetworkBehaviour
{
/// <summary>
/// The default position change threshold value.
/// Any changes above this threshold will be replicated.
/// </summary>
public const float PositionThresholdDefault = 0.001f;
/// <summary>
/// The default rotation angle change threshold value.
/// Any changes above this threshold will be replicated.
/// </summary>
public const float RotAngleThresholdDefault = 0.01f;
/// <summary>
/// The default scale change threshold value.
/// Any changes above this threshold will be replicated.
/// </summary>
public const float ScaleThresholdDefault = 0.01f;
/// <summary>
/// The handler delegate type that takes client requested changes and returns resulting changes handled by the server.
/// </summary>
/// <param name="pos">The position requested by the client.</param>
/// <param name="rot">The rotation requested by the client.</param>
/// <param name="scale">The scale requested by the client.</param>
/// <returns>The resulting position, rotation and scale changes after handling.</returns>
public delegate (Vector3 pos, Quaternion rotOut, Vector3 scale) OnClientRequestChangeDelegate(Vector3 pos, Quaternion rot, Vector3 scale);
/// <summary>
/// The handler that gets invoked when server receives a change from a client.
/// This handler would be useful for server to modify pos/rot/scale before applying client's request.
/// </summary>
public OnClientRequestChangeDelegate OnClientRequestChange;
internal struct NetworkTransformState : INetworkSerializable
@@ -244,15 +270,62 @@ namespace Unity.Netcode.Components
}
}
public bool SyncPositionX = true, SyncPositionY = true, SyncPositionZ = true;
public bool SyncRotAngleX = true, SyncRotAngleY = true, SyncRotAngleZ = true;
public bool SyncScaleX = true, SyncScaleY = true, SyncScaleZ = true;
/// <summary>
/// Whether or not x component of position will be replicated
/// </summary>
public bool SyncPositionX = true;
/// <summary>
/// Whether or not y component of position will be replicated
/// </summary>
public bool SyncPositionY = true;
/// <summary>
/// Whether or not z component of position will be replicated
/// </summary>
public bool SyncPositionZ = true;
/// <summary>
/// Whether or not x component of rotation will be replicated
/// </summary>
public bool SyncRotAngleX = true;
/// <summary>
/// Whether or not y component of rotation will be replicated
/// </summary>
public bool SyncRotAngleY = true;
/// <summary>
/// Whether or not z component of rotation will be replicated
/// </summary>
public bool SyncRotAngleZ = true;
/// <summary>
/// Whether or not x component of scale will be replicated
/// </summary>
public bool SyncScaleX = true;
/// <summary>
/// Whether or not y component of scale will be replicated
/// </summary>
public bool SyncScaleY = true;
/// <summary>
/// Whether or not z component of scale will be replicated
/// </summary>
public bool SyncScaleZ = true;
/// <summary>
/// The current position threshold value
/// Any changes to the position that exceeds the current threshold value will be replicated
/// </summary>
public float PositionThreshold = PositionThresholdDefault;
/// <summary>
/// The current rotation threshold value
/// Any changes to the rotation that exceeds the current threshold value will be replicated
/// Minimum Value: 0.001
/// Maximum Value: 360.0
/// </summary>
[Range(0.001f, 360.0f)]
public float RotAngleThreshold = RotAngleThresholdDefault;
/// <summary>
/// The current scale threshold value
/// Any changes to the scale that exceeds the current threshold value will be replicated
/// </summary>
public float ScaleThreshold = ScaleThresholdDefault;
/// <summary>
@@ -265,6 +338,10 @@ namespace Unity.Netcode.Components
public bool InLocalSpace = false;
private bool m_LastInterpolateLocal = false; // was the last frame local
/// <summary>
/// When enabled (default) interpolation is applied and when disabled no interpolation is applied
/// Note: can be changed during runtime.
/// </summary>
public bool Interpolate = true;
private bool m_LastInterpolate = true; // was the last frame interpolated
@@ -276,7 +353,17 @@ namespace Unity.Netcode.Components
/// </summary>
// This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
public bool CanCommitToTransform { get; protected set; }
/// <summary>
/// Internally used by <see cref="NetworkTransform"/> to keep track of whether this <see cref="NetworkBehaviour"/> derived class instance
/// was instantiated on the server side or not.
/// </summary>
protected bool m_CachedIsServer;
/// <summary>
/// Internally used by <see cref="NetworkTransform"/> to keep track of the <see cref="NetworkManager"/> instance assigned to this
/// this <see cref="NetworkBehaviour"/> derived class instance.
/// </summary>
protected NetworkManager m_CachedNetworkManager;
private readonly NetworkVariable<NetworkTransformState> m_ReplicatedNetworkState = new NetworkVariable<NetworkTransformState>(new NetworkTransformState());
@@ -716,6 +803,13 @@ namespace Unity.Netcode.Components
}
}
/// <summary>
/// Will set the maximum interpolation boundary for the interpolators of this <see cref="NetworkTransform"/> instance.
/// This value roughly translates to the maximum value of 't' in <see cref="Mathf.Lerp(float, float, float)"/> and
/// <see cref="Mathf.LerpUnclamped(float, float, float)"/> for all transform elements being monitored by
/// <see cref="NetworkTransform"/> (i.e. Position, Rotation, and Scale)
/// </summary>
/// <param name="maxInterpolationBound">Maximum time boundary that can be used in a frame when interpolating between two values</param>
public void SetMaxInterpolationBound(float maxInterpolationBound)
{
m_PositionXInterpolator.MaxInterpolationBound = maxInterpolationBound;
@@ -750,6 +844,8 @@ namespace Unity.Netcode.Components
}
}
/// <inheritdoc/>
public override void OnNetworkSpawn()
{
// must set up m_Transform in OnNetworkSpawn because it's possible an object spawns but is disabled
@@ -773,16 +869,19 @@ namespace Unity.Netcode.Components
Initialize();
}
/// <inheritdoc/>
public override void OnNetworkDespawn()
{
m_ReplicatedNetworkState.OnValueChanged -= OnNetworkStateChanged;
}
/// <inheritdoc/>
public override void OnGainedOwnership()
{
Initialize();
}
/// <inheritdoc/>
public override void OnLostOwnership()
{
Initialize();
@@ -850,8 +949,7 @@ namespace Unity.Netcode.Components
[ServerRpc]
private void SetStateServerRpc(Vector3 pos, Quaternion rot, Vector3 scale, bool shouldTeleport)
{
// server has received this RPC request to move change transform. Give the server a chance to modify or
// even reject the move
// server has received this RPC request to move change transform. give the server a chance to modify or even reject the move
if (OnClientRequestChange != null)
{
(pos, rot, scale) = OnClientRequestChange(pos, rot, scale);
@@ -862,8 +960,9 @@ namespace Unity.Netcode.Components
m_LocalAuthoritativeNetworkState.IsTeleportingNextFrame = shouldTeleport;
}
// todo this is currently in update, to be able to catch any transform changes. A FixedUpdate mode could be added to be less intense, but it'd be
// todo: this is currently in update, to be able to catch any transform changes. A FixedUpdate mode could be added to be less intense, but it'd be
// conditional to users only making transform update changes in FixedUpdate.
/// <inheritdoc/>
protected virtual void Update()
{
if (!IsSpawned)
@@ -921,6 +1020,10 @@ namespace Unity.Netcode.Components
/// <summary>
/// Teleports the transform to the given values without interpolating
/// </summary>
/// <param name="newPosition"></param> new position to move to.
/// <param name="newRotation"></param> new rotation to rotate to.
/// <param name="newScale">new scale to scale to.</param>
/// <exception cref="Exception"></exception>
public void Teleport(Vector3 newPosition, Quaternion newRotation, Vector3 newScale)
{
if (!CanCommitToTransform)
@@ -945,6 +1048,7 @@ namespace Unity.Netcode.Components
/// <summary>
/// Override this method and return false to switch to owner authoritative mode
/// </summary>
/// <returns>(<see cref="true"/> or <see cref="false"/>) where when false it runs as owner-client authoritative</returns>
protected virtual bool OnIsServerAuthoritative()
{
return true;

View File

@@ -151,6 +151,8 @@ namespace Unity.Netcode.Editor
}
}
/// <inheritdoc/>
public override void OnInspectorGUI()
{
if (!m_Initialized)
@@ -230,6 +232,11 @@ namespace Unity.Netcode.Editor
internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist";
/// <summary>
/// Recursively finds the root parent of a <see cref="Transform"/>
/// </summary>
/// <param name="transform">The current <see cref="Transform"/> we are inspecting for a parent</param>
/// <returns>the root parent for the first <see cref="Transform"/> passed into the method</returns>
public static Transform GetRootParentTransform(Transform transform)
{
if (transform.parent == null || transform.parent == transform)
@@ -244,6 +251,8 @@ namespace Unity.Netcode.Editor
/// does not already have a NetworkObject component. If not it will notify
/// the user that NetworkBehaviours require a NetworkObject.
/// </summary>
/// <param name="gameObject"><see cref="GameObject"/> to start checking for a <see cref="NetworkObject"/></param>
/// <param name="networkObjectRemoved">used internally</param>
public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false)
{
// If there are no NetworkBehaviours or no gameObject, then exit early

View File

@@ -6,6 +6,10 @@ using UnityEditorInternal;
namespace Unity.Netcode.Editor
{
/// <summary>
/// This <see cref="CustomEditor"/> handles the translation between the <see cref="NetworkConfig"/> and
/// the <see cref="NetworkManager"/> properties.
/// </summary>
[CustomEditor(typeof(NetworkManager), true)]
[CanEditMultipleObjects]
public class NetworkManagerEditor : UnityEditor.Editor
@@ -200,6 +204,7 @@ namespace Unity.Netcode.Editor
m_NetworkPrefabsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkPrefabs");
}
/// <inheritdoc/>
public override void OnInspectorGUI()
{
Initialize();

View File

@@ -4,6 +4,9 @@ using UnityEditor;
namespace Unity.Netcode.Editor
{
/// <summary>
/// The <see cref="CustomEditor"/> for <see cref="NetworkObject"/>
/// </summary>
[CustomEditor(typeof(NetworkObject), true)]
[CanEditMultipleObjects]
public class NetworkObjectEditor : UnityEditor.Editor
@@ -23,6 +26,7 @@ namespace Unity.Netcode.Editor
m_NetworkObject = (NetworkObject)target;
}
/// <inheritdoc/>
public override void OnInspectorGUI()
{
Initialize();

View File

@@ -4,6 +4,9 @@ using Unity.Netcode.Components;
namespace Unity.Netcode.Editor
{
/// <summary>
/// The <see cref="CustomEditor"/> for <see cref="NetworkTransform"/>
/// </summary>
[CustomEditor(typeof(NetworkTransform), true)]
public class NetworkTransformEditor : UnityEditor.Editor
{
@@ -28,6 +31,7 @@ namespace Unity.Netcode.Editor
private static GUIContent s_RotationLabel = EditorGUIUtility.TrTextContent("Rotation");
private static GUIContent s_ScaleLabel = EditorGUIUtility.TrTextContent("Scale");
/// <inheritdoc/>
public void OnEnable()
{
m_SyncPositionXProperty = serializedObject.FindProperty(nameof(NetworkTransform.SyncPositionX));
@@ -46,6 +50,7 @@ namespace Unity.Netcode.Editor
m_InterpolateProperty = serializedObject.FindProperty(nameof(NetworkTransform.Interpolate));
}
/// <inheritdoc/>
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Syncing", EditorStyles.boldLabel);

View File

@@ -150,8 +150,16 @@ namespace Unity.Netcode
/// </summary>
public bool EnableNetworkLogs = true;
/// <summary>
/// The number of RTT samples that is kept as an average for calculations
/// </summary>
public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one)
/// <summary>
/// The number of slots used for RTT calculations. This is the maximum amount of in-flight messages
/// </summary>
public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets)
/// <summary>
/// Returns a base64 encoded version of the configuration
/// </summary>

View File

@@ -470,6 +470,7 @@ namespace Unity.Netcode
/// <summary>
/// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed
/// </summary>
/// <param name="parentNetworkObject">the new <see cref="NetworkObject"/> parent</param>
public virtual void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject) { }
private bool m_VarInit = false;
@@ -751,6 +752,11 @@ namespace Unity.Netcode
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
}
/// <summary>
/// Invoked when the <see cref="GameObject"/> the <see cref="NetworkBehaviour"/> is attached to.
/// NOTE: If you override this, you will want to always invoke this base class version of this
/// <see cref="OnDestroy"/> method!!
/// </summary>
public virtual void OnDestroy()
{
// this seems odd to do here, but in fact especially in tests we can find ourselves

View File

@@ -3,6 +3,9 @@ using Unity.Profiling;
namespace Unity.Netcode
{
/// <summary>
/// An helper class that helps NetworkManager update NetworkBehaviours and replicate them down to connected clients.
/// </summary>
public class NetworkBehaviourUpdater
{
private HashSet<NetworkObject> m_Touched = new HashSet<NetworkObject>();

View File

@@ -62,6 +62,9 @@ namespace Unity.Netcode
internal Dictionary<ulong, ConnectionApprovalResponse> ClientsToApprove = new Dictionary<ulong, ConnectionApprovalResponse>();
/// <summary>
/// The <see cref="NetworkPrefabHandler"/> instance created after starting the <see cref="NetworkManager"/>
/// </summary>
public NetworkPrefabHandler PrefabHandler
{
get
@@ -197,12 +200,25 @@ namespace Unity.Netcode
return gameObject;
}
/// <summary>
/// Accessor for the <see cref="NetworkTimeSystem"/> of the NetworkManager.
/// Prefer the use of the LocalTime and ServerTime properties
/// </summary>
public NetworkTimeSystem NetworkTimeSystem { get; private set; }
/// <summary>
/// Accessor for the <see cref="NetworkTickSystem"/> of the NetworkManager.
/// </summary>
public NetworkTickSystem NetworkTickSystem { get; private set; }
/// <summary>
/// The local <see cref="NetworkTime"/>
/// </summary>
public NetworkTime LocalTime => NetworkTickSystem?.LocalTime ?? default;
/// <summary>
/// The <see cref="NetworkTime"/> on the server
/// </summary>
public NetworkTime ServerTime => NetworkTickSystem?.ServerTime ?? default;
/// <summary>
@@ -229,10 +245,19 @@ namespace Unity.Netcode
internal IDeferredMessageManager DeferredMessageManager { get; private set; }
/// <summary>
/// Gets the CustomMessagingManager for this NetworkManager
/// </summary>
public CustomMessagingManager CustomMessagingManager { get; private set; }
/// <summary>
/// The <see cref="NetworkSceneManager"/> instance created after starting the <see cref="NetworkManager"/>
/// </summary>
public NetworkSceneManager SceneManager { get; private set; }
/// <summary>
/// The client id used to represent the server
/// </summary>
public const ulong ServerClientId = 0;
/// <summary>
@@ -341,7 +366,9 @@ namespace Unity.Netcode
/// </summary>
public bool IsConnectedClient { get; internal set; }
/// <summary>
/// Can be used to determine if the <see cref="NetworkManager"/> is currently shutting itself down
/// </summary>
public bool ShutdownInProgress { get { return m_ShuttingDown; } }
/// <summary>
@@ -374,30 +401,46 @@ namespace Unity.Netcode
/// <summary>
/// Connection Approval Response
/// </summary>
/// <param name="Approved">Whether or not the client was approved</param>
/// <param name="CreatePlayerObject">If true, a player object will be created. Otherwise the client will have no object.</param>
/// <param name="PlayerPrefabHash">The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.</param>
/// <param name="Position">The position to spawn the client at. If null, the prefab position is used.</param>
/// <param name="Rotation">The rotation to spawn the client with. If null, the prefab position is used.</param>
/// <param name="Pending">If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.</param>
public class ConnectionApprovalResponse
{
/// <summary>
/// Whether or not the client was approved
/// </summary>
public bool Approved;
/// <summary>
/// If true, a player object will be created. Otherwise the client will have no object.
/// </summary>
public bool CreatePlayerObject;
/// <summary>
/// The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used.
/// </summary>
public uint? PlayerPrefabHash;
/// <summary>
/// The position to spawn the client at. If null, the prefab position is used.
/// </summary>
public Vector3? Position;
/// <summary>
/// The rotation to spawn the client with. If null, the prefab position is used.
/// </summary>
public Quaternion? Rotation;
/// <summary>
/// If the Approval decision cannot be made immediately, the client code can set Pending to true, keep a reference to the ConnectionApprovalResponse object and write to it later. Client code must exercise care to setting all the members to the value it wants before marking Pending to false, to indicate completion. If the field is set as Pending = true, we'll monitor the object until it gets set to not pending anymore and use the parameters then.
/// </summary>
public bool Pending;
}
/// <summary>
/// Connection Approval Request
/// </summary>
/// <param name="Payload">The connection data payload</param>
/// <param name="ClientNetworkId">The Network Id of the client we are about to handle</param>
public struct ConnectionApprovalRequest
{
/// <summary>
/// The connection data payload
/// </summary>
public byte[] Payload;
/// <summary>
/// The Network Id of the client we are about to handle
/// </summary>
public ulong ClientNetworkId;
}
@@ -961,6 +1004,7 @@ namespace Unity.Netcode
/// <summary>
/// Starts a server
/// </summary>
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in server mode successfully.</returns>
public bool StartServer()
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
@@ -1000,6 +1044,7 @@ namespace Unity.Netcode
/// <summary>
/// Starts a client
/// </summary>
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in client mode successfully.</returns>
public bool StartClient()
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
@@ -1033,6 +1078,7 @@ namespace Unity.Netcode
/// <summary>
/// Starts a Host
/// </summary>
/// <returns>(<see cref="true"/>/<see cref="false"/>) returns true if <see cref="NetworkManager"/> started in host mode successfully.</returns>
public bool StartHost()
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
@@ -1144,6 +1190,9 @@ namespace Unity.Netcode
return true;
}
/// <summary>
/// Set this NetworkManager instance as the static NetworkManager singleton
/// </summary>
public void SetSingleton()
{
Singleton = this;
@@ -1405,7 +1454,7 @@ namespace Unity.Netcode
ClearClients();
}
// INetworkUpdateSystem
/// <inheritdoc />
public void NetworkUpdate(NetworkUpdateStage updateStage)
{
switch (updateStage)

View File

@@ -541,16 +541,34 @@ namespace Unity.Netcode
m_LatestParent = latestParent;
}
/// <summary>
/// Set the parent of the NetworkObject transform.
/// </summary>
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
/// <returns>Whether or not reparenting was successful.</returns>
public bool TrySetParent(Transform parent, bool worldPositionStays = true)
{
return TrySetParent(parent.GetComponent<NetworkObject>(), worldPositionStays);
}
/// <summary>
/// Set the parent of the NetworkObject transform.
/// </summary>
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
/// <returns>Whether or not reparenting was successful.</returns>
public bool TrySetParent(GameObject parent, bool worldPositionStays = true)
{
return TrySetParent(parent.GetComponent<NetworkObject>(), worldPositionStays);
}
/// <summary>
/// Set the parent of the NetworkObject transform.
/// </summary>
/// <param name="parent">The new parent for this NetworkObject transform will be the child of.</param>
/// <param name="worldPositionStays">If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.</param>
/// <returns>Whether or not reparenting was successful.</returns>
public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
{
if (!AutoObjectParentSync)
@@ -813,8 +831,8 @@ namespace Unity.Netcode
// if and when we have different systems for where it is expected that orphans survive across ticks,
// then this warning will remind us that we need to revamp the system because then we can no longer simply
// spawn the orphan without its parent (at least, not when its transform is set to local coords mode)
// - because then youll have children popping at the wrong location not having their parents global position to root them
// - and then theyll pop to the correct location after they get the parent, and that would be not good
// - because then you'll have children popping at the wrong location not having their parent's global position to root them
// - and then they'll pop to the correct location after they get the parent, and that would be not good
internal static void VerifyParentingStatus()
{
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)

View File

@@ -7,25 +7,55 @@ using UnityEngine.PlayerLoop;
namespace Unity.Netcode
{
/// <summary>
/// Defines the required interface of a network update system being executed by the network update loop.
/// Defines the required interface of a network update system being executed by the <see cref="NetworkUpdateLoop"/>.
/// </summary>
public interface INetworkUpdateSystem
{
/// <summary>
/// The update method that is being executed in the context of related <see cref="NetworkUpdateStage"/>.
/// </summary>
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> that is being executed.</param>
void NetworkUpdate(NetworkUpdateStage updateStage);
}
/// <summary>
/// Defines network update stages being executed by the network update loop.
/// See for more details on update stages:
/// https://docs.unity3d.com/ScriptReference/PlayerLoop.Initialization.html
/// </summary>
public enum NetworkUpdateStage : byte
{
Unset = 0, // Default
/// <summary>
/// Default value
/// </summary>
Unset = 0,
/// <summary>
/// Very first initialization update
/// </summary>
Initialization = 1,
/// <summary>
/// Invoked before Fixed update
/// </summary>
EarlyUpdate = 2,
/// <summary>
/// Fixed Update (i.e. state machine, physics, animations, etc)
/// </summary>
FixedUpdate = 3,
/// <summary>
/// Updated before the Monobehaviour.Update for all components is invoked
/// </summary>
PreUpdate = 4,
/// <summary>
/// Updated when the Monobehaviour.Update for all components is invoked
/// </summary>
Update = 5,
/// <summary>
/// Updated before the Monobehaviour.LateUpdate for all components is invoked
/// </summary>
PreLateUpdate = 6,
/// <summary>
/// Updated after the Monobehaviour.LateUpdate for all components is invoked
/// </summary>
PostLateUpdate = 7
}
@@ -53,6 +83,7 @@ namespace Unity.Netcode
/// <summary>
/// Registers a network update system to be executed in all network update stages.
/// </summary>
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to register for all network updates</param>
public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
{
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
@@ -64,6 +95,8 @@ namespace Unity.Netcode
/// <summary>
/// Registers a network update system to be executed in a specific network update stage.
/// </summary>
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to register for all network updates</param>
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> being registered for the <see cref="INetworkUpdateSystem"/> implementation</param>
public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
{
var sysSet = s_UpdateSystem_Sets[updateStage];
@@ -94,6 +127,7 @@ namespace Unity.Netcode
/// <summary>
/// Unregisters a network update system from all network update stages.
/// </summary>
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to deregister from all network updates</param>
public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem)
{
foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage)))
@@ -105,6 +139,8 @@ namespace Unity.Netcode
/// <summary>
/// Unregisters a network update system from a specific network update stage.
/// </summary>
/// <param name="updateSystem">The <see cref="INetworkUpdateSystem"/> implementation to deregister from all network updates</param>
/// <param name="updateStage">The <see cref="NetworkUpdateStage"/> to be deregistered from the <see cref="INetworkUpdateSystem"/> implementation</param>
public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update)
{
var sysSet = s_UpdateSystem_Sets[updateStage];

View File

@@ -7,8 +7,18 @@ namespace Unity.Netcode
/// </summary>
public class InvalidParentException : Exception
{
/// <summary>
/// Constructor for <see cref="InvalidParentException"/>
/// </summary>
public InvalidParentException() { }
/// <inheritdoc/>
/// <param name="message"></param>
public InvalidParentException(string message) : base(message) { }
/// <inheritdoc/>
/// <param name="message"></param>
/// <param name="innerException"></param>
public InvalidParentException(string message, Exception innerException) : base(message, innerException) { }
}
}

View File

@@ -26,8 +26,15 @@ namespace Unity.Netcode
public SpawnStateException(string message, Exception inner) : base(message, inner) { }
}
/// <summary>
/// Exception thrown when a specified network channel is invalid
/// </summary>
public class InvalidChannelException : Exception
{
/// <summary>
/// Constructs an InvalidChannelException with a message
/// </summary>
/// <param name="message">the message</param>
public InvalidChannelException(string message) : base(message) { }
}
}

View File

@@ -14,8 +14,23 @@ namespace Unity.Netcode
public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel;
// internal logging
/// <summary>
/// Locally logs a info log with Netcode prefixing.
/// </summary>
/// <param name="message">The message to log</param>
public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}");
/// <summary>
/// Locally logs a warning log with Netcode prefixing.
/// </summary>
/// <param name="message">The message to log</param>
public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}");
/// <summary>
/// Locally logs a error log with Netcode prefixing.
/// </summary>
/// <param name="message">The message to log</param>
public static void LogError(string message) => Debug.LogError($"[Netcode] {message}");
/// <summary>

View File

@@ -193,6 +193,7 @@ namespace Unity.Netcode
/// <summary>
/// Sends a named message to all clients
/// </summary>
/// <param name="messageName">The message name to send</param>
/// <param name="messageStream">The message stream containing the data</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public void SendNamedMessageToAll(string messageName, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)

View File

@@ -3,21 +3,52 @@ using Unity.Collections;
namespace Unity.Netcode
{
/// <summary>
/// Server-Side RPC
/// Place holder. <see cref="ServerRpcParams"/>
/// Note: Clients always send to one destination when sending RPCs to the server
/// so this structure is a place holder
/// </summary>
public struct ServerRpcSendParams
{
}
/// <summary>
/// The receive parameters for server-side remote procedure calls
/// </summary>
public struct ServerRpcReceiveParams
{
/// <summary>
/// Server-Side RPC
/// The client identifier of the sender
/// </summary>
public ulong SenderClientId;
}
/// <summary>
/// Server-Side RPC
/// Can be used with any sever-side remote procedure call
/// Note: typically this is use primarily for the <see cref="ServerRpcReceiveParams"/>
/// </summary>
public struct ServerRpcParams
{
/// <summary>
/// The server RPC send parameters (currently a place holder)
/// </summary>
public ServerRpcSendParams Send;
/// <summary>
/// The client RPC receive parameters provides you with the sender's identifier
/// </summary>
public ServerRpcReceiveParams Receive;
}
/// <summary>
/// Client-Side RPC
/// The send parameters, when sending client RPCs, provides you wil the ability to
/// target specific clients as a managed or unmanaged list:
/// <see cref="TargetClientIds"/> and <see cref="TargetClientIdsNativeArray"/>
/// </summary>
public struct ClientRpcSendParams
{
/// <summary>
@@ -34,13 +65,32 @@ namespace Unity.Netcode
public NativeArray<ulong>? TargetClientIdsNativeArray;
}
/// <summary>
/// Client-Side RPC
/// Place holder. <see cref="ServerRpcParams"/>
/// Note: Server will always be the sender, so this structure is a place holder
/// </summary>
public struct ClientRpcReceiveParams
{
}
/// <summary>
/// Client-Side RPC
/// Can be used with any client-side remote procedure call
/// Note: Typically this is used primarily for sending to a specific list
/// of clients as opposed to the default (all).
/// <see cref="ClientRpcSendParams"/>
/// </summary>
public struct ClientRpcParams
{
/// <summary>
/// The client RPC send parameters provides you with the ability to send to a specific list of clients
/// </summary>
public ClientRpcSendParams Send;
/// <summary>
/// The client RPC receive parameters (currently a place holder)
/// </summary>
public ClientRpcReceiveParams Receive;
}

View File

@@ -25,8 +25,15 @@ namespace Unity.Netcode
/// </summary>
public event OnListChangedDelegate OnListChanged;
/// <summary>
/// Constructor method for <see cref="NetworkList"/>
/// </summary>
public NetworkList() { }
/// <inheritdoc/>
/// <param name="values"></param>
/// <param name="readPerm"></param>
/// <param name="writePerm"></param>
public NetworkList(IEnumerable<T> values = default,
NetworkVariableReadPermission readPerm = DefaultReadPerm,
NetworkVariableWritePermission writePerm = DefaultWritePerm)
@@ -448,6 +455,9 @@ namespace Unity.Netcode
OnListChanged?.Invoke(listEvent);
}
/// <summary>
/// This is actually unused left-over from a previous interface
/// </summary>
public int LastModifiedTick
{
get
@@ -457,6 +467,11 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Overridden <see cref="IDisposable"/> implementation.
/// CAUTION: If you derive from this class and override the <see cref="Dispose"/> method,
/// you **must** always invoke the base.Dispose() method!
/// </summary>
public override void Dispose()
{
m_List.Dispose();

View File

@@ -8,6 +8,7 @@ namespace Unity.Netcode
/// <summary>
/// A variable that can be synchronized over the network.
/// </summary>
/// <typeparam name="T">the unmanaged type for <see cref="NetworkVariable{T}"/> </typeparam>
[Serializable]
public class NetworkVariable<T> : NetworkVariableBase where T : unmanaged
{
@@ -22,7 +23,12 @@ namespace Unity.Netcode
/// </summary>
public OnValueChangedDelegate OnValueChanged;
/// <summary>
/// Constructor for <see cref="NetworkVariable{T}"/>
/// </summary>
/// <param name="value">initial value set that is of type T</param>
/// <param name="readPerm">the <see cref="NetworkVariableReadPermission"/> for this <see cref="NetworkVariable{T}"/></param>
/// <param name="writePerm">the <see cref="NetworkVariableWritePermission"/> for this <see cref="NetworkVariable{T}"/></param>
public NetworkVariable(T value = default,
NetworkVariableReadPermission readPerm = DefaultReadPerm,
NetworkVariableWritePermission writePerm = DefaultWritePerm)
@@ -31,6 +37,9 @@ namespace Unity.Netcode
m_InternalValue = value;
}
/// <summary>
/// The internal value of the NetworkVariable
/// </summary>
[SerializeField]
private protected T m_InternalValue;
@@ -58,7 +67,7 @@ namespace Unity.Netcode
}
// Compares two values of the same unmanaged type by underlying memory
// Ignoring any overriden value checks
// Ignoring any overridden value checks
// Size is fixed
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe bool ValueEquals(ref T a, ref T b)
@@ -71,7 +80,11 @@ namespace Unity.Netcode
return UnsafeUtility.MemCmp(aptr, bptr, sizeof(T)) == 0;
}
/// <summary>
/// Sets the <see cref="Value"/>, marks the <see cref="NetworkVariable{T}"/> dirty, and invokes the <see cref="OnValueChanged"/> callback
/// if there are subscribers to that event.
/// </summary>
/// <param name="value">the new value of type `T` to be set/></param>
private protected void Set(T value)
{
m_IsDirty = true;

View File

@@ -12,16 +12,36 @@ namespace Unity.Netcode
/// </summary>
internal const NetworkDelivery Delivery = NetworkDelivery.ReliableFragmentedSequenced;
/// <summary>
/// Maintains a link to the associated NetworkBehaviour
/// </summary>
private protected NetworkBehaviour m_NetworkBehaviour;
/// <summary>
/// Initializes the NetworkVariable
/// </summary>
/// <param name="networkBehaviour">The NetworkBehaviour the NetworkVariable belongs to</param>
public void Initialize(NetworkBehaviour networkBehaviour)
{
m_NetworkBehaviour = networkBehaviour;
}
/// <summary>
/// The default read permissions
/// </summary>
public const NetworkVariableReadPermission DefaultReadPerm = NetworkVariableReadPermission.Everyone;
/// <summary>
/// The default write permissions
/// </summary>
public const NetworkVariableWritePermission DefaultWritePerm = NetworkVariableWritePermission.Server;
/// <summary>
/// The default constructor for <see cref="NetworkVariableBase"/> that can be used to create a
/// custom NetworkVariable.
/// </summary>
/// <param name="readPerm">the <see cref="NetworkVariableReadPermission"/> access settings</param>
/// <param name="writePerm">the <see cref="NetworkVariableWritePermission"/> access settings</param>
protected NetworkVariableBase(
NetworkVariableReadPermission readPerm = DefaultReadPerm,
NetworkVariableWritePermission writePerm = DefaultWritePerm)
@@ -30,6 +50,10 @@ namespace Unity.Netcode
WritePerm = writePerm;
}
/// <summary>
/// The <see cref="m_IsDirty"/> property is used to determine if the
/// value of the `NetworkVariable` has changed.
/// </summary>
private protected bool m_IsDirty;
/// <summary>
@@ -43,11 +67,15 @@ namespace Unity.Netcode
/// </summary>
public readonly NetworkVariableReadPermission ReadPerm;
/// <summary>
/// The write permission for this var
/// </summary>
public readonly NetworkVariableWritePermission WritePerm;
/// <summary>
/// Sets whether or not the variable needs to be delta synced
/// </summary>
/// <param name="isDirty">Whether or not the var is dirty</param>
public virtual void SetDirty(bool isDirty)
{
m_IsDirty = isDirty;
@@ -70,6 +98,11 @@ namespace Unity.Netcode
return m_IsDirty;
}
/// <summary>
/// Gets if a specific client has permission to read the var or not
/// </summary>
/// <param name="clientId">The client id</param>
/// <returns>Whether or not the client has permission to read</returns>
public bool CanClientRead(ulong clientId)
{
switch (ReadPerm)
@@ -82,6 +115,11 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Gets if a specific client has permission to write the var or not
/// </summary>
/// <param name="clientId">The client id</param>
/// <returns>Whether or not the client has permission to write</returns>
public bool CanClientWrite(ulong clientId)
{
switch (WritePerm)
@@ -127,6 +165,9 @@ namespace Unity.Netcode
/// <param name="keepDirtyDelta">Whether or not the delta should be kept as dirty or consumed</param>
public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta);
/// <summary>
/// Virtual <see cref="IDisposable"/> implementation
/// </summary>
public virtual void Dispose()
{
}

View File

@@ -1,14 +1,32 @@
namespace Unity.Netcode
{
/// <summary>
/// The permission types for reading a var
/// </summary>
public enum NetworkVariableReadPermission
{
/// <summary>
/// Everyone can read
/// </summary>
Everyone,
/// <summary>
/// Only the owner and the server can read
/// </summary>
Owner,
}
/// <summary>
/// The permission types for writing a var
/// </summary>
public enum NetworkVariableWritePermission
{
/// <summary>
/// Only the server can write
/// </summary>
Server,
/// <summary>
/// Only the owner can write
/// </summary>
Owner
}
}

View File

@@ -120,10 +120,28 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam>
public class UserNetworkVariableSerialization<T>
{
/// <summary>
/// The write value delegate handler definition
/// </summary>
/// <param name="writer">The <see cref="FastBufferWriter"/> to write the value of type `T`</param>
/// <param name="value">The value of type `T` to be written</param>
public delegate void WriteValueDelegate(FastBufferWriter writer, in T value);
/// <summary>
/// The read value delegate handler definition
/// </summary>
/// <param name="reader">The <see cref="FastBufferReader"/> to read the value of type `T`</param>
/// <param name="value">The value of type `T` to be read</param>
public delegate void ReadValueDelegate(FastBufferReader reader, out T value);
/// <summary>
/// The <see cref="WriteValueDelegate"/> delegate handler declaration
/// </summary>
public static WriteValueDelegate WriteValue;
/// <summary>
/// The <see cref="ReadValueDelegate"/> delegate handler declaration
/// </summary>
public static ReadValueDelegate ReadValue;
}
@@ -192,6 +210,7 @@ namespace Unity.Netcode
/// but there's no way to achieve the same thing with a class, this sets up various read/write schemes
/// based on which constraints are met by `T` using reflection, which is done at module load time.
/// </summary>
/// <typeparam name="T">The type the associated NetworkVariable is templated on</typeparam>
[Serializable]
public static class NetworkVariableSerialization<T> where T : unmanaged
{

View File

@@ -935,7 +935,7 @@ namespace Unity.Netcode
/// Unloads an additively loaded scene. If you want to unload a <see cref="LoadSceneMode.Single"/> mode loaded scene load another <see cref="LoadSceneMode.Single"/> scene.
/// When applicable, the <see cref="AsyncOperation"/> is delivered within the <see cref="SceneEvent"/> via the <see cref="OnSceneEvent"/>
/// </summary>
/// <param name="sceneName">scene name to unload</param>
/// <param name="scene"></param>
/// <returns><see cref="SceneEventProgressStatus"/> (<see cref="SceneEventProgressStatus.Started"/> means it was successful)</returns>
public SceneEventProgressStatus UnloadScene(Scene scene)
{
@@ -1134,6 +1134,7 @@ namespace Unity.Netcode
/// When applicable, the <see cref="AsyncOperation"/> is delivered within the <see cref="SceneEvent"/> via <see cref="OnSceneEvent"/>
/// </summary>
/// <param name="sceneName">the name of the scene to be loaded</param>
/// <param name="loadSceneMode">how the scene will be loaded (single or additive mode)</param>
/// <returns><see cref="SceneEventProgressStatus"/> (<see cref="SceneEventProgressStatus.Started"/> means it was successful)</returns>
public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode)
{

View File

@@ -2,6 +2,9 @@ using System.Runtime.CompilerServices;
namespace Unity.Netcode
{
/// <summary>
/// Utility class to count the number of bytes or bits needed to serialize a value.
/// </summary>
public static class BitCounter
{
// Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest)

View File

@@ -62,80 +62,507 @@ namespace Unity.Netcode
return m_Implementation.GetFastBufferWriter();
}
/// <summary>
/// Read or write a string
/// </summary>
/// <param name="s">The value to read/write</param>
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
public void SerializeValue(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValue(ref s, oneByteChars);
/// <summary>
/// Read or write a single byte
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref byte value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
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);
/// <summary>
/// Read or write an array of primitive values (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
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);
/// <summary>
/// Read or write an enum value
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of enum values
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a struct value implementing ISerializeByMemcpy
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of struct values implementing ISerializeByMemcpy
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a struct or class value implementing INetworkSerializable
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of struct or class values implementing INetworkSerializable
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
public void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Vector2 value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Vector2 value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Vector2 values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Vector2[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Vector3 value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Vector3 value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Vector3 values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Vector3[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Vector2Int value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Vector2Int value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Vector2Int values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Vector2Int[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Vector3Int value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Vector3Int value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Vector3Int values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Vector3Int[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Vector4 value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Vector4 value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Vector4 values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Vector4[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Quaternion value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Quaternion value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Quaternion values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Quaternion[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Color value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Color value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Color values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Color[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Color32 value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Color32 value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Color32 values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Color32[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Ray value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Ray value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Ray values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Ray[] value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a Ray2D value
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValue(ref Ray2D value) => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write an array of Ray2D values
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValue(ref Ray2D[] value) => m_Implementation.SerializeValue(ref value);
// There are many FixedString types, but all of them share the interfaces INativeList<bool> and IUTF8Bytes.
// INativeList<bool> provides the Length property
// IUTF8Bytes provides GetUnsafePtr()
// Those two are necessary to serialize FixedStrings efficiently
// - otherwise we'd just be memcpying the whole thing even if
// - otherwise we'd just be memcpy'ing the whole thing even if
// most of it isn't used.
/// <summary>
/// Read or write a FixedString value
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of FixedStrings</param>
public void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValue(ref value);
/// <summary>
/// Read or write a NetworkSerializable value.
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The values to read/write</param>
public void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new() => m_Implementation.SerializeNetworkSerializable(ref value);
/// <summary>
/// Performs an advance check to ensure space is available to read/write one or more values.
/// This provides a performance benefit for serializing multiple values using the
/// SerializeValuePreChecked methods. But note that the benefit is small and only likely to be
/// noticeable if serializing a very large number of items.
/// </summary>
/// <param name="amount"></param>
/// <returns></returns>
public bool PreCheck(int amount)
{
return m_Implementation.PreCheck(amount);
}
/// <summary>
/// Serialize a string, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="s">The value to read/write</param>
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValuePreChecked(ref s, oneByteChars);
/// <summary>
/// Serialize a byte, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref byte value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a primitive, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
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);
/// <summary>
/// Serialize an array of primitives, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable types in an array</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of primitives</param>
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);
/// <summary>
/// Serialize an enum, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of enums</param>
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize an array of enums, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable types in an array</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of enums</param>
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a struct, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize an array of structs, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable types in an array</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution of structs</param>
public void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector2 value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector2 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValuePreChecked(ref Vector2[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector3, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector3 value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector3 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValuePreChecked(ref Vector3[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector2Int, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector2Int value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector2Int array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector3Int, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector3Int value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector3Int array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector4, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Vector4[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Quaternion, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Quaternion value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Quaternion array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Quaternion[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Color, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Color value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Color array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Color[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Color32, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Color32 value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Color32 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Color32[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Ray, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Ray value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Ray array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Ray[] value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Ray2D, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Ray2D value) => m_Implementation.SerializeValuePreChecked(ref value);
/// <summary>
/// Serialize a Ray2D array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
public void SerializeValuePreChecked(ref Ray2D[] value) => m_Implementation.SerializeValuePreChecked(ref value);
// There are many FixedString types, but all of them share the interfaces INativeList<bool> and IUTF8Bytes.
@@ -144,6 +571,16 @@ namespace Unity.Netcode
// Those two are necessary to serialize FixedStrings efficiently
// - otherwise we'd just be memcpying the whole thing even if
// most of it isn't used.
/// <summary>
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The network serializable type</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution for fixed strings</param>
public void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes => m_Implementation.SerializeValuePreChecked(ref value);
}

View File

@@ -6,6 +6,7 @@ namespace Unity.Netcode
{
/// <summary>
/// Utility class for packing values in serialization.
/// <seealso cref="ByteUnpacker"/> to unpack packed values.
/// </summary>
public static class BytePacker
{
@@ -282,14 +283,49 @@ namespace Unity.Netcode
public void WriteValueBitPacked<T>(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value);
#else
/// <summary>
/// Maximum serializable value for a BitPacked ushort (minimum for unsigned is 0)
/// </summary>
public const ushort BitPackedUshortMax = (1 << 15) - 1;
/// <summary>
/// Maximum serializable value for a BitPacked short
/// </summary>
public const short BitPackedShortMax = (1 << 14) - 1;
/// <summary>
/// Minimum serializable value size for a BitPacked ushort
/// </summary>
public const short BitPackedShortMin = -(1 << 14);
/// <summary>
/// Maximum serializable value for a BitPacked uint (minimum for unsigned is 0)
/// </summary>
public const uint BitPackedUintMax = (1 << 30) - 1;
/// <summary>
/// Maximum serializable value for a BitPacked int
/// </summary>
public const int BitPackedIntMax = (1 << 29) - 1;
/// <summary>
/// Minimum serializable value size for a BitPacked int
/// </summary>
public const int BitPackedIntMin = -(1 << 29);
/// <summary>
/// Maximum serializable value for a BitPacked ulong (minimum for unsigned is 0)
/// </summary>
public const ulong BitPackedULongMax = (1L << 61) - 1;
/// <summary>
/// Maximum serializable value for a BitPacked long
/// </summary>
public const long BitPackedLongMax = (1L << 60) - 1;
/// <summary>
/// Minimum serializable value size for a BitPacked long
/// </summary>
public const long BitPackedLongMin = -(1L << 60);
/// <summary>

View File

@@ -4,14 +4,26 @@ using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// Byte Unpacking Helper Class
/// Use this class to unpack values during deserialization for values that were packed.
/// <seealso cref="BytePacker"/> to pack unpacked values
/// </summary>
public static class ByteUnpacker
{
#if UNITY_NETCODE_DEBUG_NO_PACKING
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValuePacked<T>(FastBufferReader reader, out T value) where T: unmanaged => reader.ReadValueSafe(out value);
#else
/// <summary>
/// Read a packed enum value
/// </summary>
/// <param name="reader">The reader to read from</param>
/// <param name="value">The value that's read</param>
/// <typeparam name="TEnum">Type of enum to read</typeparam>
/// <exception cref="InvalidOperationException">Throws InvalidOperationException if an enum somehow ends up not being the size of a byte, short, int, or long (which should be impossible)</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void ReadValuePacked<TEnum>(FastBufferReader reader, out TEnum value) where TEnum : unmanaged, Enum
{
@@ -302,7 +314,7 @@ namespace Unity.Netcode
#endif
#if UNITY_NETCODE_DEBUG_NO_PACKING
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueBitPacked<T>(FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value);
#else

View File

@@ -6,6 +6,12 @@ using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// Optimized class used for reading values from a byte stream
/// <seealso cref="FastBufferWriter"/>
/// <seealso cref="BytePacker"/>
/// <seealso cref="ByteUnpacker"/>
/// </summary>
public struct FastBufferReader : IDisposable
{
internal struct ReaderHandle
@@ -87,15 +93,15 @@ namespace Unity.Netcode
/// <summary>
/// Create a FastBufferReader from a NativeArray.
///
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
/// FastBufferReader will then own the data.
///
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
/// The exception to this is when the <param name="copyAllocator"></param> 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). This is true, unless the <param name="internalAllocator"> param is explicitly set
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
/// should manually call Dispose() when it is no longer needed.
/// </summary>
@@ -162,15 +168,15 @@ namespace Unity.Netcode
/// <summary>
/// Create a FastBufferReader from an existing byte buffer.
///
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
/// FastBufferReader will then own the data.
///
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
/// The exception to this is when the <param name="copyAllocator"></param> 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). This is true, unless the <param name="internalAllocator"> param is explicitly set
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
/// should manually call Dispose() when it is no longer needed.
/// </summary>
@@ -187,15 +193,15 @@ namespace Unity.Netcode
/// <summary>
/// Create a FastBufferReader from a FastBufferWriter.
///
/// A new buffer will be created using the given <param name="copyAllocator"> and the value will be copied in.
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
/// FastBufferReader will then own the data.
///
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
/// The exception to this is when the <param name="copyAllocator"></param> 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). This is true, unless the <param name="internalAllocator"> param is explicitly set
/// stored anywhere in heap memory). This is true, unless the <param name="internalAllocator"></param> param is explicitly set
/// to i.e.: Allocator.Persistent in which case it would allow the internal data to Persist for longer, but the caller
/// should manually call Dispose() when it is no longer needed.
/// </summary>
@@ -214,10 +220,10 @@ namespace Unity.Netcode
/// want to change the copyAllocator 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 <param name="copyAllocator"> and the value will be copied in.
/// A new buffer will be created using the given <param name="copyAllocator"></param> and the value will be copied in.
/// FastBufferReader will then own the data.
///
/// The exception to this is when the <param name="copyAllocator"> passed in is Allocator.None. In this scenario,
/// The exception to this is when the <param name="copyAllocator"></param> 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
@@ -235,7 +241,7 @@ namespace Unity.Netcode
}
/// <summary>
/// Frees the allocated buffer
/// <see cref="IDisposable"/> implementation that frees the allocated buffer
/// </summary>
public unsafe void Dispose()
{
@@ -335,6 +341,7 @@ namespace Unity.Netcode
/// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following
/// operations in release builds.
/// </summary>
/// <typeparam name="T">the type `T` of the value you are trying to read</typeparam>
/// <param name="value">The value you want to read</param>
/// <returns>True if the read is allowed, false otherwise</returns>
/// <exception cref="InvalidOperationException">If called while in a bitwise context</exception>
@@ -364,7 +371,7 @@ namespace Unity.Netcode
/// Differs from TryBeginRead only in that it won't ever move the AllowedReadMark backward.
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
/// <returns>true upon success</returns>
/// <exception cref="InvalidOperationException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal unsafe bool TryBeginReadInternal(int bytes)
@@ -393,7 +400,7 @@ namespace Unity.Netcode
/// Returns an array representation of the underlying byte buffer.
/// !!Allocates a new array!!
/// </summary>
/// <returns></returns>
/// <returns>byte array</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe byte[] ToArray()
{
@@ -408,7 +415,7 @@ namespace Unity.Netcode
/// <summary>
/// Gets a direct pointer to the underlying buffer
/// </summary>
/// <returns></returns>
/// <returns><see cref="byte"/> pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe byte* GetUnsafePtr()
{
@@ -418,7 +425,7 @@ namespace Unity.Netcode
/// <summary>
/// Gets a direct pointer to the underlying buffer at the current read position
/// </summary>
/// <returns></returns>
/// <returns><see cref="byte"/> pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe byte* GetUnsafePtrAtCurrentPosition()
{
@@ -428,8 +435,8 @@ namespace Unity.Netcode
/// <summary>
/// Read an INetworkSerializable
/// </summary>
/// <param name="value">INetworkSerializable instance</param>
/// <typeparam name="T"></typeparam>
/// <param name="value">INetworkSerializable instance</param>
/// <exception cref="NotImplementedException"></exception>
public void ReadNetworkSerializable<T>(out T value) where T : INetworkSerializable, new()
{
@@ -442,7 +449,7 @@ namespace Unity.Netcode
/// Read an array of INetworkSerializables
/// </summary>
/// <param name="value">INetworkSerializable instance</param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T">the array to read the values of type `T` into</typeparam>
/// <exception cref="NotImplementedException"></exception>
public void ReadNetworkSerializable<T>(out T[] value) where T : INetworkSerializable, new()
{
@@ -537,7 +544,7 @@ namespace Unity.Netcode
/// <param name="value">Value to read</param>
/// <param name="bytesToRead">Number of bytes</param>
/// <param name="offsetBytes">Offset into the value to write the bytes</param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="T">the type value to read the value into</typeparam>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="OverflowException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -738,127 +745,522 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Read a NetworkSerializable value
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
/// <summary>
/// Read a NetworkSerializable array
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
/// <summary>
/// Read a NetworkSerializable value
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
/// <summary>
/// Read a NetworkSerializable array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value);
/// <summary>
/// Read a struct
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
/// <summary>
/// Read a struct array
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value);
/// <summary>
/// Read a struct
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a struct array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[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);
/// <summary>
/// Read a primitive value array (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[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);
/// <summary>
/// Read a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[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);
/// <summary>
/// Read a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[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);
/// <summary>
/// Read an enum value
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
/// <summary>
/// Read an enum array
/// </summary>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Read an enum value
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <summary>
/// Read an enum array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector2
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2 value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector2 array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector3
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3 value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector3 array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector2Int
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2Int value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector2Int array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector2Int[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector3Int
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3Int value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector3Int array
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector3Int[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector4
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector4 value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector4
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Vector4[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Quaternion
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Quaternion value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Quaternion array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Quaternion[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Color
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Color array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Color32
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color32 value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Color32 array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Color32[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Ray
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Ray array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Ray2D
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray2D value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Ray2D array
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValue(out Ray2D[] value) => ReadUnmanaged(out value);
/// <summary>
/// Read a Vector2
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2 value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector2 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector3
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3 value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector3 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector2Int
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2Int value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector2Int array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector2Int[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector3Int
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3Int value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector3Int array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector3Int[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector4
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector4 value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Vector4 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Vector4[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Quaternion
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Quaternion value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Quaternion array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Quaternion[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Color
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Collor array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Color32
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color32 value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Color32 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Color32[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Ray
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Ray array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray[] value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Ray2D
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray2D value) => ReadUnmanagedSafe(out value);
/// <summary>
/// Read a Ray2D array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the values to read</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ReadValueSafe(out Ray2D[] value) => ReadUnmanagedSafe(out value);
@@ -868,6 +1270,17 @@ namespace Unity.Netcode
// Those two are necessary to serialize FixedStrings efficiently
// - otherwise we'd just be memcpying the whole thing even if
// most of it isn't used.
/// <summary>
/// Read a FixedString value.
/// This method is a little difficult to use, since you have to know the size of the string before
/// reading it, but is useful when the string is a known, fixed size. Note that the size of the
/// string is also encoded, so the size to call TryBeginRead on is actually the fixed size (in bytes)
/// plus sizeof(int)
/// </summary>
/// <param name="value">the value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValue<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes
@@ -878,6 +1291,16 @@ namespace Unity.Netcode
ReadBytes(value.GetUnsafePtr(), length);
}
/// <summary>
/// Read a FixedString value.
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple reads at once by calling TryBeginRead.
/// </summary>
/// <param name="value">the value to read</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void ReadValueSafe<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes

View File

@@ -6,6 +6,12 @@ using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// Optimized class used for writing values into a byte stream
/// <seealso cref="FastBufferReader"/>
/// <seealso cref="BytePacker"/>
/// <seealso cref="ByteUnpacker"/>
/// </summary>
public struct FastBufferWriter : IDisposable
{
internal struct WriterHandle
@@ -108,7 +114,7 @@ namespace Unity.Netcode
}
/// <summary>
/// Frees the allocated buffer
/// <see cref="IDisposable"/> implementation that frees the allocated buffer
/// </summary>
public unsafe void Dispose()
{
@@ -267,7 +273,8 @@ namespace Unity.Netcode
/// operations in release builds. Instead, attempting to write past the marked position in release builds
/// will write to random memory and cause undefined behavior, likely including instability and crashes.
/// </summary>
/// <param name="value">The value you want to write</param>
/// <typeparam name="T">The value type to write</typeparam>
/// <param name="value">The value of the type `T` you want to write</param>
/// <returns>True if the write is allowed, false otherwise</returns>
/// <exception cref="InvalidOperationException">If called while in a bitwise context</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -792,151 +799,590 @@ namespace Unity.Netcode
}
}
// 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.
/// <summary>
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
/// methods exist with the same signature, it causes a compile error because they would end up
/// being emitted as the same method, even if the constraints are different.
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
/// which then allows the compiler to do overload resolution based on the generic constraints
/// without the user having to pass the struct in themselves.
/// </summary>
public struct ForPrimitives
{
}
/// <summary>
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
/// methods exist with the same signature, it causes a compile error because they would end up
/// being emitted as the same method, even if the constraints are different.
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
/// which then allows the compiler to do overload resolution based on the generic constraints
/// without the user having to pass the struct in themselves.
/// </summary>
public struct ForEnums
{
}
/// <summary>
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
/// methods exist with the same signature, it causes a compile error because they would end up
/// being emitted as the same method, even if the constraints are different.
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
/// which then allows the compiler to do overload resolution based on the generic constraints
/// without the user having to pass the struct in themselves.
/// </summary>
public struct ForStructs
{
}
/// <summary>
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
/// methods exist with the same signature, it causes a compile error because they would end up
/// being emitted as the same method, even if the constraints are different.
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
/// which then allows the compiler to do overload resolution based on the generic constraints
/// without the user having to pass the struct in themselves.
/// </summary>
public struct ForNetworkSerializable
{
}
/// <summary>
/// This empty struct exists to allow overloading WriteValue based on generic constraints.
/// At the bytecode level, constraints aren't included in the method signature, so if multiple
/// methods exist with the same signature, it causes a compile error because they would end up
/// being emitted as the same method, even if the constraints are different.
/// Adding an empty struct with a default value gives them different signatures in the bytecode,
/// which then allows the compiler to do overload resolution based on the generic constraints
/// without the user having to pass the struct in themselves.
/// </summary>
public struct ForFixedStrings
{
}
/// <summary>
/// Write a NetworkSerializable value
/// </summary>
/// <param name="value">The value to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
/// <summary>
/// Write a NetworkSerializable array
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
/// <summary>
/// Write a NetworkSerializable value
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
/// <summary>
/// Write a NetworkSerializable array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value);
/// <summary>
/// Write a struct
/// </summary>
/// <param name="value">The value to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
/// <summary>
/// Write a struct array
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value);
/// <summary>
/// Write a struct
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
/// <summary>
/// Write a struct array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value);
/// <summary>
/// Write a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The value to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
/// <summary>
/// Write a primitive value array (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanaged(value);
/// <summary>
/// Write a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
/// <summary>
/// Write a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T> => WriteUnmanagedSafe(value);
/// <summary>
/// Write an enum value
/// </summary>
/// <param name="value">The value to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
/// <summary>
/// Write an enum array
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value);
/// <summary>
/// Write an enum value
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
/// <summary>
/// Write an enum array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">The values to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector2
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector2 value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector2 array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector2[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector3
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector3 value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector3 array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector3[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector2Int
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector2Int value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector2Int array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector2Int[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector3Int
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector3Int value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector3Int array
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector3Int[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector4
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Vector4 value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector4
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Vector4[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Quaternion
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Quaternion value) => WriteUnmanaged(value);
/// <summary>
/// Write a Quaternion array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Quaternion[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Color
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Color value) => WriteUnmanaged(value);
/// <summary>
/// Write a Color array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Color[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Color32
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Color32 value) => WriteUnmanaged(value);
/// <summary>
/// Write a Color32 array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Color32[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Ray
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Ray value) => WriteUnmanaged(value);
/// <summary>
/// Write a Ray array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Ray[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Ray2D
/// </summary>
/// <param name="value">the value to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(in Ray2D value) => WriteUnmanaged(value);
/// <summary>
/// Write a Ray2D array
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValue(Ray2D[] value) => WriteUnmanaged(value);
/// <summary>
/// Write a Vector2
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector2 value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector2 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector2[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector3
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector3 value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector3 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector3[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector2Int
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector2Int value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector2Int array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector2Int[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector3Int
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector3Int value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector3Int array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector3Int[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector4
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Vector4 value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Vector4 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Vector4[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Quaternion
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Quaternion value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Quaternion array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Quaternion[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Color
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Color value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Collor array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Color[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Color32
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Color32 value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Color32 array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Color32[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Ray
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Ray value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Ray array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Ray[] value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Ray2D
///
/// "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 write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(in Ray2D value) => WriteUnmanagedSafe(value);
/// <summary>
/// Write a Ray2D array
///
/// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking
/// for multiple writes at once by calling TryBeginWrite.
/// </summary>
/// <param name="value">the values to write</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe(Ray2D[] value) => WriteUnmanagedSafe(value);
@@ -946,6 +1392,15 @@ namespace Unity.Netcode
// Those two are necessary to serialize FixedStrings efficiently
// - otherwise we'd just be memcpying the whole thing even if
// most of it isn't used.
/// <summary>
/// Write a FixedString value. Writes only the part of the string that's actually used.
/// When calling TryBeginWrite, ensure you calculate the write size correctly (preferably by calling
/// FastBufferWriter.GetWriteSize())
/// </summary>
/// <param name="value">the value to write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void WriteValue<T>(in T value, ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes
@@ -960,6 +1415,16 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Write a FixedString value. Writes only the part of the string that's actually used.
///
/// "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 write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteValueSafe<T>(in T value, ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes

View File

@@ -9,26 +9,58 @@ namespace Unity.Netcode
/// <typeparam name="T"></typeparam>
public struct ForceNetworkSerializeByMemcpy<T> : INetworkSerializeByMemcpy, IEquatable<ForceNetworkSerializeByMemcpy<T>> where T : unmanaged, IEquatable<T>
{
/// <summary>
/// The wrapped value
/// </summary>
public T Value;
/// <summary>
/// The default constructor for <see cref="ForceNetworkSerializeByMemcpy{T}"/>
/// </summary>
/// <param name="value">sets the initial value of type `T`</param>
public ForceNetworkSerializeByMemcpy(T value)
{
Value = value;
}
/// <summary>
/// Convert implicitly from the ForceNetworkSerializeByMemcpy wrapper to the underlying value
/// </summary>
/// <param name="container">The wrapper</param>
/// <returns>The underlying value</returns>
public static implicit operator T(ForceNetworkSerializeByMemcpy<T> container) => container.Value;
/// <summary>
/// Convert implicitly from a T value to a ForceNetworkSerializeByMemcpy wrapper
/// </summary>
/// <param name="underlyingValue">the value</param>
/// <returns>a new wrapper</returns>
public static implicit operator ForceNetworkSerializeByMemcpy<T>(T underlyingValue) => new ForceNetworkSerializeByMemcpy<T> { Value = underlyingValue };
/// <summary>
/// Check if wrapped values are equal
/// </summary>
/// <param name="other">Other wrapper</param>
/// <returns>true if equal</returns>
public bool Equals(ForceNetworkSerializeByMemcpy<T> other)
{
return Value.Equals(other.Value);
}
/// <summary>
/// Check if this value is equal to a boxed object value
/// </summary>
/// <param name="obj">The boxed value to check against</param>
/// <returns>true if equal</returns>
public override bool Equals(object obj)
{
return obj is ForceNetworkSerializeByMemcpy<T> other && Equals(other);
}
/// <summary>
/// Obtains the wrapped value's hash code
/// </summary>
/// <returns>Wrapped value's hash code</returns>
public override int GetHashCode()
{
return Value.GetHashCode();

View File

@@ -6,7 +6,7 @@ namespace Unity.Netcode
/// 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
/// like `NativeList&lt;T&gt;`), it should be serialized via `INetworkSerializable` or via
/// `FastBufferReader`/`FastBufferWriter` extension methods.
/// </summary>
public interface INetworkSerializeByMemcpy

View File

@@ -4,81 +4,536 @@ using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// Interface for an implementation of one side of a two-way serializer
/// </summary>
public interface IReaderWriter
{
/// <summary>
/// Check whether this implementation is a "reader" - if it's been constructed to deserialize data
/// </summary>
bool IsReader { get; }
/// <summary>
/// Check whether this implementation is a "writer" - if it's been constructed to serialize data
/// </summary>
bool IsWriter { get; }
/// <summary>
/// Get the underlying FastBufferReader struct.
/// Only valid when IsReader == true
/// </summary>
/// <returns>underlying FastBufferReader</returns>
FastBufferReader GetFastBufferReader();
/// <summary>
/// Get the underlying FastBufferWriter struct.
/// Only valid when IsWriter == true
/// </summary>
/// <returns>underlying FastBufferWriter</returns>
FastBufferWriter GetFastBufferWriter();
/// <summary>
/// Read or write a string
/// </summary>
/// <param name="s">The value to read/write</param>
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
void SerializeValue(ref string s, bool oneByteChars = false);
/// <summary>
/// Read or write a single byte
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref byte value);
/// <summary>
/// Read or write a primitive value (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
/// <summary>
/// Read or write an array of primitive values (int, bool, etc)
/// Accepts any value that implements the given interfaces, but is not guaranteed to work correctly
/// on values that are not primitives.
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
/// <summary>
/// Read or write an enum value
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
/// <summary>
/// Read or write an array of enum values
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
/// <summary>
/// Read or write a struct value implementing ISerializeByMemcpy
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
/// <summary>
/// Read or write an array of struct values implementing ISerializeByMemcpy
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
/// <summary>
/// Read or write a struct or class value implementing INetworkSerializable
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
/// <summary>
/// Read or write an array of struct or class values implementing INetworkSerializable
/// </summary>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new();
/// <summary>
/// Read or write a FixedString value
/// </summary>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
/// <typeparam name="T">The type being serialized</typeparam>
void SerializeValue<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
/// <summary>
/// Read or write a Vector2 value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Vector2 value);
/// <summary>
/// Read or write an array of Vector2 values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Vector2[] value);
/// <summary>
/// Read or write a Vector3 value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Vector3 value);
/// <summary>
/// Read or write an array of Vector3 values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Vector3[] value);
/// <summary>
/// Read or write a Vector2Int value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Vector2Int value);
/// <summary>
/// Read or write an array of Vector2Int values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Vector2Int[] value);
/// <summary>
/// Read or write a Vector3Int value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Vector3Int value);
/// <summary>
/// Read or write an array of Vector3Int values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Vector3Int[] value);
/// <summary>
/// Read or write a Vector4 value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Vector4 value);
/// <summary>
/// Read or write an array of Vector4 values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Vector4[] value);
/// <summary>
/// Read or write a Quaternion value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Quaternion value);
/// <summary>
/// Read or write an array of Quaternion values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Quaternion[] value);
/// <summary>
/// Read or write a Color value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Color value);
/// <summary>
/// Read or write an array of Color values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Color[] value);
/// <summary>
/// Read or write a Color32 value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Color32 value);
/// <summary>
/// Read or write an array of Color32 values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Color32[] value);
/// <summary>
/// Read or write a Ray value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Ray value);
/// <summary>
/// Read or write an array of Ray values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Ray[] value);
/// <summary>
/// Read or write a Ray2D value
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValue(ref Ray2D value);
/// <summary>
/// Read or write an array of Ray2D values
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValue(ref Ray2D[] value);
// Has to have a different name to avoid conflicting with "where T: unmananged"
/// <summary>
/// Read or write a NetworkSerializable value.
/// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only.
/// </summary>
/// <param name="value">The value to read/write</param>
/// <typeparam name="T">The network serializable type</typeparam>
void SerializeNetworkSerializable<T>(ref T value) where T : INetworkSerializable, new();
/// <summary>
/// Performs an advance check to ensure space is available to read/write one or more values.
/// This provides a performance benefit for serializing multiple values using the
/// SerializeValuePreChecked methods. But note that the benefit is small and only likely to be
/// noticeable if serializing a very large number of items.
/// </summary>
/// <param name="amount"></param>
/// <returns></returns>
bool PreCheck(int amount);
/// <summary>
/// Serialize a string, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="s">The value to read/write</param>
/// <param name="oneByteChars">If true, characters will be limited to one-byte ASCII characters</param>
void SerializeValuePreChecked(ref string s, bool oneByteChars = false);
/// <summary>
/// Serialize a byte, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref byte value);
/// <summary>
/// Serialize a primitive, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
/// <summary>
/// Serialize an array of primitives, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable<T>, IEquatable<T>;
/// <summary>
/// Serialize an enum, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
/// <summary>
/// Serialize an array of enums, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum;
/// <summary>
/// Serialize a struct, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
/// <summary>
/// Serialize an array of structs, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The values to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy;
/// <summary>
/// Serialize a FixedString, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <typeparam name="T">The type being serialized</typeparam>
/// <param name="value">The value to read/write</param>
/// <param name="unused">An unused parameter that can be used for enabling overload resolution based on generic constraints</param>
void SerializeValuePreChecked<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
where T : unmanaged, INativeList<byte>, IUTF8Bytes;
/// <summary>
/// Serialize a Vector2, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector2 value);
/// <summary>
/// Serialize a Vector2 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValuePreChecked(ref Vector2[] value);
/// <summary>
/// Serialize a Vector3, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector3 value);
/// <summary>
/// Serialize a Vector3 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValuePreChecked(ref Vector3[] value);
/// <summary>
/// Serialize a Vector2Int, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector2Int value);
/// <summary>
/// Serialize a Vector2Int array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The values to read/write</param>
void SerializeValuePreChecked(ref Vector2Int[] value);
/// <summary>
/// Serialize a Vector3Int, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector3Int value);
/// <summary>
/// Serialize a Vector3Int array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector3Int[] value);
/// <summary>
/// Serialize a Vector4, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector4 value);
/// <summary>
/// Serialize a Vector4Array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Vector4[] value);
/// <summary>
/// Serialize a Quaternion, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Quaternion value);
/// <summary>
/// Serialize a Quaternion array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Quaternion[] value);
/// <summary>
/// Serialize a Color, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Color value);
/// <summary>
/// Serialize a Color array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Color[] value);
/// <summary>
/// Serialize a Color32, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Color32 value);
/// <summary>
/// Serialize a Color32 array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Color32[] value);
/// <summary>
/// Serialize a Ray, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Ray value);
/// <summary>
/// Serialize a Ray array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Ray[] value);
/// <summary>
/// Serialize a Ray2D, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Ray2D value);
/// <summary>
/// Serialize a Ray2D array, "pre-checked", which skips buffer checks.
/// In debug and editor builds, a check is made to ensure you've called "PreCheck" before
/// calling this. In release builds, calling this without calling "PreCheck" may read or write
/// past the end of the buffer, which will cause memory corruption and undefined behavior.
/// </summary>
/// <param name="value">The value to read/write</param>
void SerializeValuePreChecked(ref Ray2D[] value);
}
}

View File

@@ -96,8 +96,18 @@ namespace Unity.Netcode
serializer.SerializeValue(ref m_NetworkBehaviourId);
}
/// <summary>
/// Implicitly convert <see cref="NetworkBehaviourReference"/> to <see cref="NetworkBehaviour"/>.
/// </summary>
/// <param name="networkBehaviourRef">The <see cref="NetworkBehaviourReference"/> to convert from.</param>
/// <returns>The <see cref="NetworkBehaviour"/> this class is holding a reference to</returns>
public static implicit operator NetworkBehaviour(NetworkBehaviourReference networkBehaviourRef) => GetInternal(networkBehaviourRef);
/// <summary>
/// Implicitly convert <see cref="NetworkBehaviour"/> to <see cref="NetworkBehaviourReference"/>.
/// </summary>
/// <param name="networkBehaviour">The <see cref="NetworkBehaviour"/> to convert from.</param>
/// <returns>The <see cref="NetworkBehaviourReference"/> created from the <see cref="NetworkBehaviour"/> passed in as a parameter</returns>
public static implicit operator NetworkBehaviourReference(NetworkBehaviour networkBehaviour) => new NetworkBehaviourReference(networkBehaviour);
}
}

View File

@@ -120,12 +120,32 @@ namespace Unity.Netcode
serializer.SerializeValue(ref m_NetworkObjectId);
}
/// <summary>
/// Implicitly convert <see cref="NetworkObjectReference"/> to <see cref="NetworkObject"/>.
/// </summary>
/// <param name="networkObjectRef">The <see cref="NetworkObjectReference"/> to convert from.</param>
/// <returns>The <see cref="NetworkObject"/> the <see cref="NetworkObjectReference"/> is referencing</returns>
public static implicit operator NetworkObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef);
/// <summary>
/// Implicitly convert <see cref="NetworkObject"/> to <see cref="NetworkObjectReference"/>.
/// </summary>
/// <param name="networkObject">The <see cref="NetworkObject"/> to convert from.</param>
/// <returns>The <see cref="NetworkObjectReference"/> created from the <see cref="NetworkObject"/> parameter</returns>
public static implicit operator NetworkObjectReference(NetworkObject networkObject) => new NetworkObjectReference(networkObject);
/// <summary>
/// Implicitly convert <see cref="NetworkObjectReference"/> to <see cref="GameObject"/>.
/// </summary>
/// <param name="networkObjectRef">The <see cref="NetworkObjectReference"/> to convert from.</param>
/// <returns>This returns the <see cref="GameObject"/> that the <see cref="NetworkObject"/> is attached to and is referenced by the <see cref="NetworkObjectReference"/> passed in as a parameter</returns>
public static implicit operator GameObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef).gameObject;
/// <summary>
/// Implicitly convert <see cref="GameObject"/> to <see cref="NetworkObject"/>.
/// </summary>
/// <param name="gameObject">The <see cref="GameObject"/> to convert from.</param>
/// <returns>The <see cref="NetworkObjectReference"/> created from the <see cref="GameObject"/> parameter that has a <see cref="NetworkObject"/> component attached to it</returns>
public static implicit operator NetworkObjectReference(GameObject gameObject) => new NetworkObjectReference(gameObject);
}
}

View File

@@ -126,6 +126,7 @@ namespace Unity.Netcode
/// Returns a list of all NetworkObjects that belong to a client.
/// </summary>
/// <param name="clientId">the client's id <see cref="NetworkManager.LocalClientId"/></param>
/// <returns>returns the list of <see cref="NetworkObject"/>s owned by the client</returns>
public List<NetworkObject> GetClientOwnedObjects(ulong clientId)
{
if (!OwnershipToObjectsTable.ContainsKey(clientId))
@@ -172,9 +173,11 @@ namespace Unity.Netcode
return GetPlayerNetworkObject(NetworkManager.LocalClientId);
}
/// <summary>
/// Returns the player object with a given clientId or null if one does not exist. This is only valid server side.
/// </summary>
/// <param name="clientId">the client identifier of the player</param>
/// <returns>The player object with a given clientId or null if one does not exist</returns>
public NetworkObject GetPlayerNetworkObject(ulong clientId)
{

View File

@@ -3,6 +3,10 @@ using Unity.Profiling;
namespace Unity.Netcode
{
/// <summary>
/// Provides discretized time.
/// This is useful for games that require ticks happening at regular interval on the server and clients.
/// </summary>
public class NetworkTickSystem
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
@@ -69,6 +73,8 @@ namespace Unity.Netcode
/// <summary>
/// Called after advancing the time system to run ticks based on the difference in time.
/// </summary>
/// <param name="localTimeSec">The local time in seconds</param>
/// <param name="serverTimeSec">The server time in seconds</param>
public void UpdateTick(double localTimeSec, double serverTimeSec)
{
// store old local tick to know how many fixed ticks passed

View File

@@ -108,6 +108,11 @@ namespace Unity.Netcode
return new NetworkTime(m_TickRate, m_CachedTick);
}
/// <summary>
/// Returns the time a number of ticks in the past.
/// </summary>
/// <param name="ticks">The number of ticks ago we're querying the time</param>
/// <returns></returns>
public NetworkTime TimeTicksAgo(int ticks)
{
return this - new NetworkTime(TickRate, ticks);
@@ -132,16 +137,34 @@ namespace Unity.Netcode
}
}
/// <summary>
/// Computes the time difference between two ticks
/// </summary>
/// <param name="a">End time</param>
/// <param name="b">Start time</param>
/// <returns>The time difference between start and end</returns>
public static NetworkTime operator -(NetworkTime a, NetworkTime b)
{
return new NetworkTime(a.TickRate, a.Time - b.Time);
}
/// <summary>
/// Computes the sum of two times
/// </summary>
/// <param name="a">First time</param>
/// <param name="b">Second time</param>
/// <returns>The sum of the two times passed in</returns>
public static NetworkTime operator +(NetworkTime a, NetworkTime b)
{
return new NetworkTime(a.TickRate, a.Time + b.Time);
}
/// <summary>
/// Computes the time a number of seconds later
/// </summary>
/// <param name="a">The start time</param>
/// <param name="b">The number of seconds to add</param>
/// <returns>The resulting time</returns>
public static NetworkTime operator +(NetworkTime a, double b)
{
a.m_TimeSec += b;
@@ -149,6 +172,12 @@ namespace Unity.Netcode
return a;
}
/// <summary>
/// Computes the time a number of seconds before
/// </summary>
/// <param name="a">The start time</param>
/// <param name="b">The number of seconds to remove</param>
/// <returns>The resulting time</returns>
public static NetworkTime operator -(NetworkTime a, double b)
{
return a + -b;

View File

@@ -36,12 +36,27 @@ namespace Unity.Netcode
/// Gets or sets the ratio at which the NetworkTimeSystem speeds up or slows down time.
/// </summary>
public double AdjustmentRatio { get; set; }
/// <summary>
/// The current local time with the local time offset applied
/// </summary>
public double LocalTime => m_TimeSec + m_CurrentLocalTimeOffset;
/// <summary>
/// The current server time with the server time offset applied
/// </summary>
public double ServerTime => m_TimeSec + m_CurrentServerTimeOffset;
internal double LastSyncedServerTimeSec { get; private set; }
internal double LastSyncedRttSec { get; private set; }
/// <summary>
/// The constructor class for <see cref="NetworkTickSystem"/>
/// </summary>
/// <param name="localBufferSec">The amount of time, in seconds, the server should buffer incoming client messages.</param>
/// <param name="serverBufferSec">The amount of the time in seconds the client should buffer incoming messages from the server.</param>
/// <param name="hardResetThresholdSec">The threshold, in seconds, used to force a hard catchup of network time.</param>
/// <param name="adjustmentRatio">The ratio at which the NetworkTimeSystem speeds up or slows down time.</param>
public NetworkTimeSystem(double localBufferSec, double serverBufferSec, double hardResetThresholdSec, double adjustmentRatio = 0.01d)
{
LocalBufferSec = localBufferSec;

View File

@@ -3,6 +3,11 @@ using UnityEngine;
namespace Unity.Netcode
{
/// <summary>
/// The generic transport class all Netcode for GameObjects network transport implementations
/// derive from. Use this class to add a custom transport.
/// <seealso cref="Transports.UTP.UnityTransport"> for an example of how a transport is integrated</seealso>
/// </summary>
public abstract class NetworkTransport : MonoBehaviour
{
/// <summary>
@@ -45,7 +50,7 @@ namespace Unity.Netcode
}
/// <summary>
/// Send a payload to the specified clientId, data and channelName.
/// Send a payload to the specified clientId, data and networkDelivery.
/// </summary>
/// <param name="clientId">The clientId to send to</param>
/// <param name="payload">The data to send</param>
@@ -64,11 +69,13 @@ namespace Unity.Netcode
/// <summary>
/// Connects client to the server
/// </summary>
/// <returns>Returns success or failure</returns>
public abstract bool StartClient();
/// <summary>
/// Starts to listening for incoming clients
/// </summary>
/// <returns>Returns success or failure</returns>
public abstract bool StartServer();
/// <summary>

View File

@@ -1,8 +1,17 @@
namespace Unity.Netcode.Transports.UTP
{
/// <summary>
/// Caching structure to track network metrics related information.
/// </summary>
public struct NetworkMetricsContext
{
/// <summary>
/// The number of packet sent.
/// </summary>
public uint PacketSentCount;
/// <summary>
/// The number of packet received.
/// </summary>
public uint PacketReceivedCount;
}
}

View File

@@ -16,6 +16,14 @@ namespace Unity.Netcode.Transports.UTP
/// </summary>
public interface INetworkStreamDriverConstructor
{
/// <summary>
/// Creates the internal NetworkDriver
/// </summary>
/// <param name="transport">The owner transport</param>
/// <param name="driver">The driver</param>
/// <param name="unreliableFragmentedPipeline">The UnreliableFragmented NetworkPipeline</param>
/// <param name="unreliableSequencedFragmentedPipeline">The UnreliableSequencedFragmented NetworkPipeline</param>
/// <param name="reliableSequencedPipeline">The ReliableSequenced NetworkPipeline</param>
void CreateDriver(
UnityTransport transport,
out NetworkDriver driver,
@@ -24,6 +32,9 @@ namespace Unity.Netcode.Transports.UTP
out NetworkPipeline reliableSequencedPipeline);
}
/// <summary>
/// Helper utility class to convert <see cref="Networking.Transport"/> error codes to human readable error messages.
/// </summary>
public static class ErrorUtilities
{
private const string k_NetworkSuccess = "Success";
@@ -37,6 +48,12 @@ namespace Unity.Netcode.Transports.UTP
private const string k_NetworkSendHandleInvalid = "Invalid NetworkInterface Send Handle. Likely caused by pipeline send data corruption.";
private const string k_NetworkArgumentMismatch = "Invalid NetworkEndpoint Arguments.";
/// <summary>
/// Convert error code to human readable error message.
/// </summary>
/// <param name="error">Status code of the error</param>
/// <param name="connectionId">Subject connection ID of the error</param>
/// <returns>Human readable error message.</returns>
public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId)
{
switch (error)
@@ -67,11 +84,24 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// The Netcode for GameObjects NetworkTransport for UnityTransport.
/// Note: This is highly recommended to use over UNet.
/// </summary>
public partial class UnityTransport : NetworkTransport, INetworkStreamDriverConstructor
{
/// <summary>
/// Enum type stating the type of protocol
/// </summary>
public enum ProtocolType
{
/// <summary>
/// Unity Transport Protocol
/// </summary>
UnityTransport,
/// <summary>
/// Unity Transport Protocol over Relay
/// </summary>
RelayUnityTransport,
}
@@ -82,15 +112,34 @@ namespace Unity.Netcode.Transports.UTP
Connected,
}
/// <summary>
/// The default maximum (receive) packet queue size
/// </summary>
public const int InitialMaxPacketQueueSize = 128;
/// <summary>
/// The default maximum payload size
/// </summary>
public const int InitialMaxPayloadSize = 6 * 1024;
/// <summary>
/// The default maximum send queue size
/// </summary>
public const int InitialMaxSendQueueSize = 16 * InitialMaxPayloadSize;
private static ConnectionAddressData s_DefaultConnectionAddressData = new ConnectionAddressData { Address = "127.0.0.1", Port = 7777, ServerListenAddress = string.Empty };
#pragma warning disable IDE1006 // Naming Styles
/// <summary>
/// The global <see cref="INetworkStreamDriverConstructor"/> implementation
/// </summary>
public static INetworkStreamDriverConstructor s_DriverConstructor;
#pragma warning restore IDE1006 // Naming Styles
/// <summary>
/// Returns either the global <see cref="INetworkStreamDriverConstructor"/> implementation or the current <see cref="UnityTransport"/> instance
/// </summary>
public INetworkStreamDriverConstructor DriverConstructor => s_DriverConstructor ?? this;
[Tooltip("Which protocol should be selected (Relay/Non-Relay).")]
@@ -187,17 +236,29 @@ namespace Unity.Netcode.Transports.UTP
set => m_DisconnectTimeoutMS = value;
}
/// <summary>
/// Structure to store the address to connect to
/// </summary>
[Serializable]
public struct ConnectionAddressData
{
/// <summary>
/// IP address of the server (address to which clients will connect to).
/// </summary>
[Tooltip("IP address of the server (address to which clients will connect to).")]
[SerializeField]
public string Address;
/// <summary>
/// UDP port of the server.
/// </summary>
[Tooltip("UDP port of the server.")]
[SerializeField]
public ushort Port;
/// <summary>
/// IP address the server will listen on. If not provided, will use 'Address'.
/// </summary>
[Tooltip("IP address the server will listen on. If not provided, will use 'Address'.")]
[SerializeField]
public string ServerListenAddress;
@@ -213,29 +274,58 @@ namespace Unity.Netcode.Transports.UTP
return endpoint;
}
/// <summary>
/// Endpoint (IP address and port) clients will connect to.
/// </summary>
public NetworkEndPoint ServerEndPoint => ParseNetworkEndpoint(Address, Port);
/// <summary>
/// Endpoint (IP address and port) server will listen/bind on.
/// </summary>
public NetworkEndPoint ListenEndPoint => ParseNetworkEndpoint((ServerListenAddress == string.Empty) ? Address : ServerListenAddress, Port);
}
/// <summary>
/// The connection (address) data for this <see cref="UnityTransport"/> instance.
/// This is where you can change IP Address, Port, or server's listen address.
/// <see cref="ConnectionAddressData"/>
/// </summary>
public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData;
/// <summary>
/// Parameters for the Network Simulator
/// </summary>
[Serializable]
public struct SimulatorParameters
{
/// <summary>
/// Delay to add to every send and received packet (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.
/// </summary>
[Tooltip("Delay to add to every send and received packet (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.")]
[SerializeField]
public int PacketDelayMS;
/// <summary>
/// Jitter (random variation) to add/substract to the packet delay (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.
/// </summary>
[Tooltip("Jitter (random variation) to add/substract to the packet delay (in milliseconds). Only applies in the editor and in development builds. The value is ignored in production builds.")]
[SerializeField]
public int PacketJitterMS;
/// <summary>
/// Percentage of sent and received packets to drop. Only applies in the editor and in the editor and in developments builds.
/// </summary>
[Tooltip("Percentage of sent and received packets to drop. Only applies in the editor and in the editor and in developments builds.")]
[SerializeField]
public int PacketDropRate;
}
/// <summary>
/// Can be used to simulate poor network conditions such as:
/// - packet delay/latency
/// - packet jitter (variances in latency, see: https://en.wikipedia.org/wiki/Jitter)
/// - packet drop rate (packet loss)
/// </summary>
public SimulatorParameters DebugSimulator = new SimulatorParameters
{
PacketDelayMS = 0,
@@ -261,8 +351,14 @@ namespace Unity.Netcode.Transports.UTP
private NetworkPipeline m_UnreliableSequencedFragmentedPipeline;
private NetworkPipeline m_ReliableSequencedPipeline;
/// <summary>
/// The client id used to represent the server.
/// </summary>
public override ulong ServerClientId => m_ServerClientId;
/// <summary>
/// The current ProtocolType used by the transport
/// </summary>
public ProtocolType Protocol => m_ProtocolType;
private RelayServerData m_RelayServerData;
@@ -428,6 +524,14 @@ namespace Unity.Netcode.Transports.UTP
m_ProtocolType = inProtocol;
}
/// <summary>Set the relay server data for the server.</summary>
/// <param name="ipv4Address">IP address of the relay server.</param>
/// <param name="port">UDP port of the relay server.</param>
/// <param name="allocationIdBytes">Allocation ID as a byte array.</param>
/// <param name="keyBytes">Allocation key as a byte array.</param>
/// <param name="connectionDataBytes">Connection data as a byte array.</param>
/// <param name="hostConnectionDataBytes">The HostConnectionData as a byte array.</param>
/// <param name="isSecure">Whether the connection is secure (uses DTLS).</param>
public void SetRelayServerData(string ipv4Address, ushort port, byte[] allocationIdBytes, byte[] keyBytes, byte[] connectionDataBytes, byte[] hostConnectionDataBytes = null, bool isSecure = false)
{
RelayConnectionData hostConnectionData;
@@ -489,6 +593,9 @@ namespace Unity.Netcode.Transports.UTP
/// <summary>
/// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call <see cref="SetRelayServerData"/>
/// </summary>
/// <param name="ipv4Address">The remote IP address</param>
/// <param name="port">The remote port</param>
/// <param name="listenAddress">The local listen address</param>
public void SetConnectionData(string ipv4Address, ushort port, string listenAddress = null)
{
ConnectionData = new ConnectionAddressData
@@ -504,6 +611,8 @@ namespace Unity.Netcode.Transports.UTP
/// <summary>
/// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call <see cref="SetRelayServerData"/>
/// </summary>
/// <param name="endPoint">The remote end point</param>
/// <param name="listenEndPoint">The local listen endpoint</param>
public void SetConnectionData(NetworkEndPoint endPoint, NetworkEndPoint listenEndPoint = default)
{
string serverAddress = endPoint.Address.Split(':')[0];
@@ -916,6 +1025,9 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// Disconnects the local client from the remote
/// </summary>
public override void DisconnectLocalClient()
{
if (m_State == State.Connected)
@@ -940,6 +1052,10 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// Disconnects a remote client from the server
/// </summary>
/// <param name="clientId">The client to disconnect</param>
public override void DisconnectRemoteClient(ulong clientId)
{
Debug.Assert(m_State == State.Listening, "DisconnectRemoteClient should be called on a listening server");
@@ -959,6 +1075,11 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// Gets the current RTT for a specific client
/// </summary>
/// <param name="clientId">The client RTT to get</param>
/// <returns>The RTT</returns>
public override ulong GetCurrentRtt(ulong clientId)
{
// We don't know if this is getting called from inside NGO (which presumably knows to
@@ -979,6 +1100,10 @@ namespace Unity.Netcode.Transports.UTP
return (ulong)ExtractRtt(ParseClientId(clientId));
}
/// <summary>
/// Initializes the transport
/// </summary>
/// <param name="networkManager">The NetworkManager that initialized and owns the transport</param>
public override void Initialize(NetworkManager networkManager = null)
{
Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf<NetworkConnection>(), "Netcode connection id size does not match UTP connection id size");
@@ -1000,6 +1125,13 @@ namespace Unity.Netcode.Transports.UTP
#endif
}
/// <summary>
/// Polls for incoming events, with an extra output parameter to report the precise time the event was received.
/// </summary>
/// <param name="clientId">The clientId this event is for</param>
/// <param name="payload">The incoming data payload</param>
/// <param name="receiveTime">The time the event was received, as reported by Time.realtimeSinceStartup.</param>
/// <returns>Returns the event type</returns>
public override NetcodeNetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
{
clientId = default;
@@ -1008,6 +1140,12 @@ namespace Unity.Netcode.Transports.UTP
return NetcodeNetworkEvent.Nothing;
}
/// <summary>
/// Send a payload to the specified clientId, data and networkDelivery.
/// </summary>
/// <param name="clientId">The clientId to send to</param>
/// <param name="payload">The data to send</param>
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
public override void Send(ulong clientId, ArraySegment<byte> payload, NetworkDelivery networkDelivery)
{
if (payload.Count > m_MaxPayloadSize)
@@ -1069,6 +1207,14 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// Connects client to the server
/// Note:
/// When this method returns false it could mean:
/// - You are trying to start a client that is already started
/// - It failed during the initial port binding when attempting to begin to connect
/// </summary>
/// <returns>true if the client was started and false if it failed to start the client</returns>
public override bool StartClient()
{
if (m_Driver.IsCreated)
@@ -1084,6 +1230,14 @@ namespace Unity.Netcode.Transports.UTP
return succeeded;
}
/// <summary>
/// Starts to listening for incoming clients
/// Note:
/// When this method returns false it could mean:
/// - You are trying to start a client that is already started
/// - It failed during the initial port binding when attempting to begin to connect
/// </summary>
/// <returns>true if the server was started and false if it failed to start the server</returns>
public override bool StartServer()
{
if (m_Driver.IsCreated)
@@ -1113,6 +1267,9 @@ namespace Unity.Netcode.Transports.UTP
}
}
/// <summary>
/// Shuts down the transport
/// </summary>
public override void Shutdown()
{
if (!m_Driver.IsCreated)
@@ -1152,6 +1309,14 @@ namespace Unity.Netcode.Transports.UTP
);
}
/// <summary>
/// Creates the internal NetworkDriver
/// </summary>
/// <param name="transport">The owner transport</param>
/// <param name="driver">The driver</param>
/// <param name="unreliableFragmentedPipeline">The UnreliableFragmented NetworkPipeline</param>
/// <param name="unreliableSequencedFragmentedPipeline">The UnreliableSequencedFragmented NetworkPipeline</param>
/// <param name="reliableSequencedPipeline">The ReliableSequenced NetworkPipeline</param>
public void CreateDriver(UnityTransport transport, out NetworkDriver driver,
out NetworkPipeline unreliableFragmentedPipeline,
out NetworkPipeline unreliableSequencedFragmentedPipeline,

View File

@@ -2,22 +2,22 @@
"name": "com.unity.netcode.gameobjects",
"displayName": "Netcode for GameObjects",
"description": "Netcode for GameObjects is a high-level netcode SDK that provides networking capabilities to GameObject/MonoBehaviour workflows within Unity and sits on top of underlying transport layer.",
"version": "1.0.0-pre.10",
"version": "1.0.0",
"unity": "2020.3",
"dependencies": {
"com.unity.nuget.mono-cecil": "1.10.1",
"com.unity.transport": "1.1.0"
},
"_upm": {
"changelog": "### Added\n\n- Added a new `OnTransportFailure` callback to `NetworkManager`. This callback is invoked when the manager's `NetworkTransport` encounters an unrecoverable error. Transport failures also cause the `NetworkManager` to shut down. Currently, this is only used by `UnityTransport` to signal a timeout of its connection to the Unity Relay servers. (#1994)\n- Added `NetworkEvent.TransportFailure`, which can be used by implementations of `NetworkTransport` to signal to `NetworkManager` that an unrecoverable error was encountered. (#1994)\n- Added test to ensure a warning occurs when nesting NetworkObjects in a NetworkPrefab (#1969)\n- Added `NetworkManager.RemoveNetworkPrefab(...)` to remove a prefab from the prefabs list (#1950)\n\n### Changed\n\n- Updated `UnityTransport` dependency on `com.unity.transport` to 1.1.0. (#2025)\n- (API Breaking) `ConnectionApprovalCallback` is no longer an `event` and will not allow more than 1 handler registered at a time. Also, `ConnectionApprovalCallback` is now a `Func<>` taking `ConnectionApprovalRequest` in and returning `ConnectionApprovalResponse` back out (#1972)\n\n### Removed\n\n### Fixed\n- Fixed issue where dynamically spawned `NetworkObject`s could throw an exception if the scene of origin handle was zero (0) and the `NetworkObject` was already spawned. (#2017)\n- Fixed issue where `NetworkObject.Observers` was not being cleared when despawned. (#2009)\n- Fixed `NetworkAnimator` could not run in the server authoritative mode. (#2003)\n- Fixed issue where late joining clients would get a soft synchronization error if any in-scene placed NetworkObjects were parented under another `NetworkObject`. (#1985)\n- Fixed issue where `NetworkBehaviourReference` would throw a type cast exception if using `NetworkBehaviourReference.TryGet` and the component type was not found. (#1984)\n- Fixed `NetworkSceneManager` was not sending scene event notifications for the currently active scene and any additively loaded scenes when loading a new scene in `LoadSceneMode.Single` mode. (#1975)\n- Fixed issue where one or more clients disconnecting during a scene event would cause `LoadEventCompleted` or `UnloadEventCompleted` to wait until the `NetworkConfig.LoadSceneTimeOut` period before being triggered. (#1973)\n- Fixed issues when multiple `ConnectionApprovalCallback`s were registered (#1972)\n- Fixed a regression in serialization support: `FixedString`, `Vector2Int`, and `Vector3Int` types can now be used in NetworkVariables and RPCs again without requiring a `ForceNetworkSerializeByMemcpy<>` wrapper. (#1961)\n- Fixed generic types that inherit from NetworkBehaviour causing crashes at compile time. (#1976)\n- Fixed endless dialog boxes when adding a `NetworkBehaviour` to a `NetworkManager` or vice-versa. (#1947)\n- Fixed `NetworkAnimator` issue where it was only synchronizing parameters if the layer or state changed or was transitioning between states. (#1946)\n- Fixed `NetworkAnimator` issue where when it did detect a parameter had changed it would send all parameters as opposed to only the parameters that changed. (#1946)\n- Fixed `NetworkAnimator` issue where it was not always disposing the `NativeArray` that is allocated when spawned. (#1946)\n- Fixed `NetworkAnimator` issue where it was not taking the animation speed or state speed multiplier into consideration. (#1946)\n- Fixed `NetworkAnimator` issue where it was not properly synchronizing late joining clients if they joined while `Animator` was transitioning between states. (#1946)\n- Fixed `NetworkAnimator` issue where the server was not relaying changes to non-owner clients when a client was the owner. (#1946)\n- Fixed issue where the `PacketLoss` metric for tools would return the packet loss over a connection lifetime instead of a single frame. (#2004)"
"changelog": "### Changed\n\n- Changed version to 1.0.0. (#2046)"
},
"upmCi": {
"footprint": "13bd9771fd94050d43c32b238d73617da4b2389d"
"footprint": "382d762a40cdcb42ebaf495e373effb00362baf1"
},
"repository": {
"url": "https://github.com/Unity-Technologies/com.unity.netcode.gameobjects.git",
"type": "git",
"revision": "d1302ce0b946a675f49425dbd11ee2c4afe89d3f"
"revision": "fddb7cd920e1db9e49d44846d7121e38f59bd137"
},
"samples": [
{