From 18ffd5fdc8e480494cb9e207b7fc9c17d2205aad Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Mon, 27 Jun 2022 00:00:00 +0000 Subject: [PATCH] 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) --- CHANGELOG.md | 8 +- .../BufferedLinearInterpolator.cs | 33 +- Components/NetworkAnimator.cs | 1 + Components/NetworkTransform.cs | 120 ++++- Editor/NetworkBehaviourEditor.cs | 9 + Editor/NetworkManagerEditor.cs | 5 + Editor/NetworkObjectEditor.cs | 4 + Editor/NetworkTransformEditor.cs | 5 + Runtime/Configuration/NetworkConfig.cs | 8 + Runtime/Core/NetworkBehaviour.cs | 6 + Runtime/Core/NetworkBehaviourUpdater.cs | 3 + Runtime/Core/NetworkManager.cs | 69 ++- Runtime/Core/NetworkObject.cs | 22 +- Runtime/Core/NetworkUpdateLoop.cs | 40 +- Runtime/Exceptions/InvalidParentException.cs | 10 + Runtime/Exceptions/SpawnStateException.cs | 7 + Runtime/Logging/NetworkLog.cs | 15 + Runtime/Messaging/CustomMessageManager.cs | 1 + Runtime/Messaging/RpcParams.cs | 50 ++ .../Collections/NetworkList.cs | 15 + Runtime/NetworkVariable/NetworkVariable.cs | 19 +- .../NetworkVariable/NetworkVariableBase.cs | 41 ++ .../NetworkVariablePermission.cs | 18 + .../NetworkVariableSerialization.cs | 19 + .../SceneManagement/NetworkSceneManager.cs | 3 +- Runtime/Serialization/BitCounter.cs | 3 + Runtime/Serialization/BufferSerializer.cs | 439 +++++++++++++++- Runtime/Serialization/BytePacker.cs | 36 ++ Runtime/Serialization/ByteUnpacker.cs | 16 +- Runtime/Serialization/FastBufferReader.cs | 465 ++++++++++++++++- Runtime/Serialization/FastBufferWriter.cs | 479 +++++++++++++++++- .../ForceNetworkSerializeByMemcpy.cs | 32 ++ .../INetworkSerializeByMemcpy.cs | 2 +- Runtime/Serialization/IReaderWriter.cs | 457 ++++++++++++++++- .../NetworkBehaviourReference.cs | 10 + .../Serialization/NetworkObjectReference.cs | 20 + Runtime/Spawning/NetworkSpawnManager.cs | 3 + Runtime/Timing/NetworkTickSystem.cs | 6 + Runtime/Timing/NetworkTime.cs | 29 ++ Runtime/Timing/NetworkTimeSystem.cs | 15 + Runtime/Transports/NetworkTransport.cs | 9 +- .../Transports/UTP/NetworkMetricsContext.cs | 9 + Runtime/Transports/UTP/UnityTransport.cs | 165 ++++++ package.json | 8 +- 44 files changed, 2667 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a49e1df..765dfed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Components/Interpolator/BufferedLinearInterpolator.cs b/Components/Interpolator/BufferedLinearInterpolator.cs index c1d7e6b..dc4919b 100644 --- a/Components/Interpolator/BufferedLinearInterpolator.cs +++ b/Components/Interpolator/BufferedLinearInterpolator.cs @@ -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 /// + /// The type of interpolated value public abstract class BufferedLinearInterpolator where T : struct { internal float MaxInterpolationBound = 3.0f; @@ -24,7 +25,7 @@ namespace Unity.Netcode } /// - /// 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. + /// 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. /// public float MaximumInterpolationTime = 0.1f; @@ -73,7 +74,7 @@ namespace Unity.Netcode private bool InvalidState => m_Buffer.Count == 0 && m_LifetimeConsumedCount == 0; /// - /// Resets Interpolator to initial state + /// Resets interpolator to initial state /// public void Clear() { @@ -85,6 +86,8 @@ namespace Unity.Netcode /// /// Teleports current interpolation value to targetValue. /// + /// The target value to teleport instantly + /// The current server time public void ResetTo(T targetValue, double serverTime) { m_LifetimeConsumedCount = 1; @@ -159,6 +162,7 @@ namespace Unity.Netcode /// /// time since call /// current server time + /// The newly interpolated value of type 'T' public T Update(float deltaTime, NetworkTime serverTime) { return Update(deltaTime, serverTime.TimeTicksAgo(1).Time, serverTime.Time); @@ -170,6 +174,7 @@ namespace Unity.Netcode /// time since last call /// our current time /// current server time + /// The newly interpolated value of type 'T' public T Update(float deltaTime, double renderTime, double serverTime) { TryConsumeFromBuffer(renderTime, serverTime); @@ -222,6 +227,8 @@ namespace Unity.Netcode /// /// Add measurements to be used during interpolation. These will be buffered before being made available to be displayed as "latest value". /// + /// The new measurement value to use + /// The time to record for measurement public void AddMeasurement(T newMeasurement, double sentTime) { m_NbItemsReceivedThisFrame++; @@ -251,6 +258,7 @@ namespace Unity.Netcode /// /// Gets latest value from the interpolator. This is updated every update as time goes by. /// + /// The current interpolated value of type 'T' public T GetInterpolatedValue() { return m_CurrentInterpValue; @@ -259,33 +267,54 @@ namespace Unity.Netcode /// /// Method to override and adapted to the generic type. This assumes interpolation for that value will be clamped. /// + /// The start value (min) + /// The end value (max) + /// The time value used to interpolate between start and end values (pos) + /// The interpolated value protected abstract T Interpolate(T start, T end, float time); + /// /// Method to override and adapted to the generic type. This assumes interpolation for that value will not be clamped. /// + /// The start value (min) + /// The end value (max) + /// The time value used to interpolate between start and end values (pos) + /// The interpolated value protected abstract T InterpolateUnclamped(T start, T end, float time); } + /// + /// + /// This is a buffered linear interpolator for a type value + /// public class BufferedLinearInterpolatorFloat : BufferedLinearInterpolator { + /// protected override float InterpolateUnclamped(float start, float end, float time) { return Mathf.LerpUnclamped(start, end, time); } + /// protected override float Interpolate(float start, float end, float time) { return Mathf.Lerp(start, end, time); } } + /// + /// + /// This is a buffered linear interpolator for a type value + /// public class BufferedLinearInterpolatorQuaternion : BufferedLinearInterpolator { + /// protected override Quaternion InterpolateUnclamped(Quaternion start, Quaternion end, float time) { return Quaternion.SlerpUnclamped(start, end, time); } + /// protected override Quaternion Interpolate(Quaternion start, Quaternion end, float time) { return Quaternion.SlerpUnclamped(start, end, time); diff --git a/Components/NetworkAnimator.cs b/Components/NetworkAnimator.cs index 2db2c80..0b138ed 100644 --- a/Components/NetworkAnimator.cs +++ b/Components/NetworkAnimator.cs @@ -37,6 +37,7 @@ namespace Unity.Netcode.Components m_SendTriggerUpdates.Clear(); } + /// public void NetworkUpdate(NetworkUpdateStage updateStage) { switch (updateStage) diff --git a/Components/NetworkTransform.cs b/Components/NetworkTransform.cs index 68a01d2..82c1ab8 100644 --- a/Components/NetworkTransform.cs +++ b/Components/NetworkTransform.cs @@ -5,20 +5,46 @@ using UnityEngine; namespace Unity.Netcode.Components { /// - /// 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. /// [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 { + /// + /// The default position change threshold value. + /// Any changes above this threshold will be replicated. + /// public const float PositionThresholdDefault = 0.001f; + + /// + /// The default rotation angle change threshold value. + /// Any changes above this threshold will be replicated. + /// public const float RotAngleThresholdDefault = 0.01f; + + /// + /// The default scale change threshold value. + /// Any changes above this threshold will be replicated. + /// public const float ScaleThresholdDefault = 0.01f; + /// + /// The handler delegate type that takes client requested changes and returns resulting changes handled by the server. + /// + /// The position requested by the client. + /// The rotation requested by the client. + /// The scale requested by the client. + /// The resulting position, rotation and scale changes after handling. public delegate (Vector3 pos, Quaternion rotOut, Vector3 scale) OnClientRequestChangeDelegate(Vector3 pos, Quaternion rot, Vector3 scale); + + /// + /// 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. + /// 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; + /// + /// Whether or not x component of position will be replicated + /// + public bool SyncPositionX = true; + /// + /// Whether or not y component of position will be replicated + /// + public bool SyncPositionY = true; + /// + /// Whether or not z component of position will be replicated + /// + public bool SyncPositionZ = true; + /// + /// Whether or not x component of rotation will be replicated + /// + public bool SyncRotAngleX = true; + /// + /// Whether or not y component of rotation will be replicated + /// + public bool SyncRotAngleY = true; + /// + /// Whether or not z component of rotation will be replicated + /// + public bool SyncRotAngleZ = true; + /// + /// Whether or not x component of scale will be replicated + /// + public bool SyncScaleX = true; + /// + /// Whether or not y component of scale will be replicated + /// + public bool SyncScaleY = true; + /// + /// Whether or not z component of scale will be replicated + /// + public bool SyncScaleZ = true; + /// + /// The current position threshold value + /// Any changes to the position that exceeds the current threshold value will be replicated + /// public float PositionThreshold = PositionThresholdDefault; + /// + /// 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 + /// [Range(0.001f, 360.0f)] public float RotAngleThreshold = RotAngleThresholdDefault; + /// + /// The current scale threshold value + /// Any changes to the scale that exceeds the current threshold value will be replicated + /// public float ScaleThreshold = ScaleThresholdDefault; /// @@ -265,6 +338,10 @@ namespace Unity.Netcode.Components public bool InLocalSpace = false; private bool m_LastInterpolateLocal = false; // was the last frame local + /// + /// When enabled (default) interpolation is applied and when disabled no interpolation is applied + /// Note: can be changed during runtime. + /// public bool Interpolate = true; private bool m_LastInterpolate = true; // was the last frame interpolated @@ -276,7 +353,17 @@ namespace Unity.Netcode.Components /// // 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; } + + /// + /// Internally used by to keep track of whether this derived class instance + /// was instantiated on the server side or not. + /// protected bool m_CachedIsServer; + + /// + /// Internally used by to keep track of the instance assigned to this + /// this derived class instance. + /// protected NetworkManager m_CachedNetworkManager; private readonly NetworkVariable m_ReplicatedNetworkState = new NetworkVariable(new NetworkTransformState()); @@ -716,6 +803,13 @@ namespace Unity.Netcode.Components } } + /// + /// Will set the maximum interpolation boundary for the interpolators of this instance. + /// This value roughly translates to the maximum value of 't' in and + /// for all transform elements being monitored by + /// (i.e. Position, Rotation, and Scale) + /// + /// Maximum time boundary that can be used in a frame when interpolating between two values public void SetMaxInterpolationBound(float maxInterpolationBound) { m_PositionXInterpolator.MaxInterpolationBound = maxInterpolationBound; @@ -750,6 +844,8 @@ namespace Unity.Netcode.Components } } + + /// 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(); } + /// public override void OnNetworkDespawn() { m_ReplicatedNetworkState.OnValueChanged -= OnNetworkStateChanged; } + /// public override void OnGainedOwnership() { Initialize(); } + /// 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. + /// protected virtual void Update() { if (!IsSpawned) @@ -921,6 +1020,10 @@ namespace Unity.Netcode.Components /// /// Teleports the transform to the given values without interpolating /// + /// new position to move to. + /// new rotation to rotate to. + /// new scale to scale to. + /// public void Teleport(Vector3 newPosition, Quaternion newRotation, Vector3 newScale) { if (!CanCommitToTransform) @@ -945,6 +1048,7 @@ namespace Unity.Netcode.Components /// /// Override this method and return false to switch to owner authoritative mode /// + /// ( or ) where when false it runs as owner-client authoritative protected virtual bool OnIsServerAuthoritative() { return true; diff --git a/Editor/NetworkBehaviourEditor.cs b/Editor/NetworkBehaviourEditor.cs index bfbdf68..7da70ea 100644 --- a/Editor/NetworkBehaviourEditor.cs +++ b/Editor/NetworkBehaviourEditor.cs @@ -151,6 +151,8 @@ namespace Unity.Netcode.Editor } } + + /// public override void OnInspectorGUI() { if (!m_Initialized) @@ -230,6 +232,11 @@ namespace Unity.Netcode.Editor internal const string AutoAddNetworkObjectIfNoneExists = "AutoAdd-NetworkObject-When-None-Exist"; + /// + /// Recursively finds the root parent of a + /// + /// The current we are inspecting for a parent + /// the root parent for the first passed into the method 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. /// + /// to start checking for a + /// used internally public static void CheckForNetworkObject(GameObject gameObject, bool networkObjectRemoved = false) { // If there are no NetworkBehaviours or no gameObject, then exit early diff --git a/Editor/NetworkManagerEditor.cs b/Editor/NetworkManagerEditor.cs index 348d7fe..a788cb8 100644 --- a/Editor/NetworkManagerEditor.cs +++ b/Editor/NetworkManagerEditor.cs @@ -6,6 +6,10 @@ using UnityEditorInternal; namespace Unity.Netcode.Editor { + /// + /// This handles the translation between the and + /// the properties. + /// [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"); } + /// public override void OnInspectorGUI() { Initialize(); diff --git a/Editor/NetworkObjectEditor.cs b/Editor/NetworkObjectEditor.cs index 6b00164..5111e92 100644 --- a/Editor/NetworkObjectEditor.cs +++ b/Editor/NetworkObjectEditor.cs @@ -4,6 +4,9 @@ using UnityEditor; namespace Unity.Netcode.Editor { + /// + /// The for + /// [CustomEditor(typeof(NetworkObject), true)] [CanEditMultipleObjects] public class NetworkObjectEditor : UnityEditor.Editor @@ -23,6 +26,7 @@ namespace Unity.Netcode.Editor m_NetworkObject = (NetworkObject)target; } + /// public override void OnInspectorGUI() { Initialize(); diff --git a/Editor/NetworkTransformEditor.cs b/Editor/NetworkTransformEditor.cs index 6510c6c..6ba27ea 100644 --- a/Editor/NetworkTransformEditor.cs +++ b/Editor/NetworkTransformEditor.cs @@ -4,6 +4,9 @@ using Unity.Netcode.Components; namespace Unity.Netcode.Editor { + /// + /// The for + /// [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"); + /// 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)); } + /// public override void OnInspectorGUI() { EditorGUILayout.LabelField("Syncing", EditorStyles.boldLabel); diff --git a/Runtime/Configuration/NetworkConfig.cs b/Runtime/Configuration/NetworkConfig.cs index 208b705..7c01888 100644 --- a/Runtime/Configuration/NetworkConfig.cs +++ b/Runtime/Configuration/NetworkConfig.cs @@ -150,8 +150,16 @@ namespace Unity.Netcode /// public bool EnableNetworkLogs = true; + /// + /// The number of RTT samples that is kept as an average for calculations + /// public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one) + + /// + /// The number of slots used for RTT calculations. This is the maximum amount of in-flight messages + /// public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets) + /// /// Returns a base64 encoded version of the configuration /// diff --git a/Runtime/Core/NetworkBehaviour.cs b/Runtime/Core/NetworkBehaviour.cs index 64babb1..d2ec920 100644 --- a/Runtime/Core/NetworkBehaviour.cs +++ b/Runtime/Core/NetworkBehaviour.cs @@ -470,6 +470,7 @@ namespace Unity.Netcode /// /// Gets called when the parent NetworkObject of this NetworkBehaviour's NetworkObject has changed /// + /// the new parent 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; } + /// + /// Invoked when the the is attached to. + /// NOTE: If you override this, you will want to always invoke this base class version of this + /// method!! + /// public virtual void OnDestroy() { // this seems odd to do here, but in fact especially in tests we can find ourselves diff --git a/Runtime/Core/NetworkBehaviourUpdater.cs b/Runtime/Core/NetworkBehaviourUpdater.cs index 6433f95..56bdfdc 100644 --- a/Runtime/Core/NetworkBehaviourUpdater.cs +++ b/Runtime/Core/NetworkBehaviourUpdater.cs @@ -3,6 +3,9 @@ using Unity.Profiling; namespace Unity.Netcode { + /// + /// An helper class that helps NetworkManager update NetworkBehaviours and replicate them down to connected clients. + /// public class NetworkBehaviourUpdater { private HashSet m_Touched = new HashSet(); diff --git a/Runtime/Core/NetworkManager.cs b/Runtime/Core/NetworkManager.cs index 226ac68..b2403fd 100644 --- a/Runtime/Core/NetworkManager.cs +++ b/Runtime/Core/NetworkManager.cs @@ -62,6 +62,9 @@ namespace Unity.Netcode internal Dictionary ClientsToApprove = new Dictionary(); + /// + /// The instance created after starting the + /// public NetworkPrefabHandler PrefabHandler { get @@ -197,12 +200,25 @@ namespace Unity.Netcode return gameObject; } + /// + /// Accessor for the of the NetworkManager. + /// Prefer the use of the LocalTime and ServerTime properties + /// public NetworkTimeSystem NetworkTimeSystem { get; private set; } + /// + /// Accessor for the of the NetworkManager. + /// public NetworkTickSystem NetworkTickSystem { get; private set; } + /// + /// The local + /// public NetworkTime LocalTime => NetworkTickSystem?.LocalTime ?? default; + /// + /// The on the server + /// public NetworkTime ServerTime => NetworkTickSystem?.ServerTime ?? default; /// @@ -229,10 +245,19 @@ namespace Unity.Netcode internal IDeferredMessageManager DeferredMessageManager { get; private set; } + /// + /// Gets the CustomMessagingManager for this NetworkManager + /// public CustomMessagingManager CustomMessagingManager { get; private set; } + /// + /// The instance created after starting the + /// public NetworkSceneManager SceneManager { get; private set; } + /// + /// The client id used to represent the server + /// public const ulong ServerClientId = 0; /// @@ -341,7 +366,9 @@ namespace Unity.Netcode /// public bool IsConnectedClient { get; internal set; } - + /// + /// Can be used to determine if the is currently shutting itself down + /// public bool ShutdownInProgress { get { return m_ShuttingDown; } } /// @@ -374,30 +401,46 @@ namespace Unity.Netcode /// /// Connection Approval Response /// - /// Whether or not the client was approved - /// If true, a player object will be created. Otherwise the client will have no object. - /// The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used. - /// The position to spawn the client at. If null, the prefab position is used. - /// The rotation to spawn the client with. If null, the prefab position is used. - /// 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. public class ConnectionApprovalResponse { + /// + /// Whether or not the client was approved + /// public bool Approved; + /// + /// If true, a player object will be created. Otherwise the client will have no object. + /// public bool CreatePlayerObject; + /// + /// The prefabHash to use for the client. If createPlayerObject is false, this is ignored. If playerPrefabHash is null, the default player prefab is used. + /// public uint? PlayerPrefabHash; + /// + /// The position to spawn the client at. If null, the prefab position is used. + /// public Vector3? Position; + /// + /// The rotation to spawn the client with. If null, the prefab position is used. + /// public Quaternion? Rotation; + /// + /// 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. + /// public bool Pending; } /// /// Connection Approval Request /// - /// The connection data payload - /// The Network Id of the client we are about to handle public struct ConnectionApprovalRequest { + /// + /// The connection data payload + /// public byte[] Payload; + /// + /// The Network Id of the client we are about to handle + /// public ulong ClientNetworkId; } @@ -961,6 +1004,7 @@ namespace Unity.Netcode /// /// Starts a server /// + /// (/) returns true if started in server mode successfully. public bool StartServer() { if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) @@ -1000,6 +1044,7 @@ namespace Unity.Netcode /// /// Starts a client /// + /// (/) returns true if started in client mode successfully. public bool StartClient() { if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) @@ -1033,6 +1078,7 @@ namespace Unity.Netcode /// /// Starts a Host /// + /// (/) returns true if started in host mode successfully. public bool StartHost() { if (NetworkLog.CurrentLogLevel <= LogLevel.Developer) @@ -1144,6 +1190,9 @@ namespace Unity.Netcode return true; } + /// + /// Set this NetworkManager instance as the static NetworkManager singleton + /// public void SetSingleton() { Singleton = this; @@ -1405,7 +1454,7 @@ namespace Unity.Netcode ClearClients(); } - // INetworkUpdateSystem + /// public void NetworkUpdate(NetworkUpdateStage updateStage) { switch (updateStage) diff --git a/Runtime/Core/NetworkObject.cs b/Runtime/Core/NetworkObject.cs index 606476f..4b20130 100644 --- a/Runtime/Core/NetworkObject.cs +++ b/Runtime/Core/NetworkObject.cs @@ -541,16 +541,34 @@ namespace Unity.Netcode m_LatestParent = latestParent; } + /// + /// Set the parent of the NetworkObject transform. + /// + /// The new parent for this NetworkObject transform will be the child of. + /// 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. + /// Whether or not reparenting was successful. public bool TrySetParent(Transform parent, bool worldPositionStays = true) { return TrySetParent(parent.GetComponent(), worldPositionStays); } + /// + /// Set the parent of the NetworkObject transform. + /// + /// The new parent for this NetworkObject transform will be the child of. + /// 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. + /// Whether or not reparenting was successful. public bool TrySetParent(GameObject parent, bool worldPositionStays = true) { return TrySetParent(parent.GetComponent(), worldPositionStays); } + /// + /// Set the parent of the NetworkObject transform. + /// + /// The new parent for this NetworkObject transform will be the child of. + /// 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. + /// Whether or not reparenting was successful. 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 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 + // - 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) diff --git a/Runtime/Core/NetworkUpdateLoop.cs b/Runtime/Core/NetworkUpdateLoop.cs index b9c52c1..81473ed 100644 --- a/Runtime/Core/NetworkUpdateLoop.cs +++ b/Runtime/Core/NetworkUpdateLoop.cs @@ -7,25 +7,55 @@ using UnityEngine.PlayerLoop; namespace Unity.Netcode { /// - /// 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 . /// public interface INetworkUpdateSystem { + /// + /// The update method that is being executed in the context of related . + /// + /// The that is being executed. void NetworkUpdate(NetworkUpdateStage updateStage); } /// /// 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 /// public enum NetworkUpdateStage : byte { - Unset = 0, // Default + /// + /// Default value + /// + Unset = 0, + /// + /// Very first initialization update + /// Initialization = 1, + /// + /// Invoked before Fixed update + /// EarlyUpdate = 2, + /// + /// Fixed Update (i.e. state machine, physics, animations, etc) + /// FixedUpdate = 3, + /// + /// Updated before the Monobehaviour.Update for all components is invoked + /// PreUpdate = 4, + /// + /// Updated when the Monobehaviour.Update for all components is invoked + /// Update = 5, + /// + /// Updated before the Monobehaviour.LateUpdate for all components is invoked + /// PreLateUpdate = 6, + /// + /// Updated after the Monobehaviour.LateUpdate for all components is invoked + /// PostLateUpdate = 7 } @@ -53,6 +83,7 @@ namespace Unity.Netcode /// /// Registers a network update system to be executed in all network update stages. /// + /// The implementation to register for all network updates public static void RegisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem) { foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage))) @@ -64,6 +95,8 @@ namespace Unity.Netcode /// /// Registers a network update system to be executed in a specific network update stage. /// + /// The implementation to register for all network updates + /// The being registered for the implementation public static void RegisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update) { var sysSet = s_UpdateSystem_Sets[updateStage]; @@ -94,6 +127,7 @@ namespace Unity.Netcode /// /// Unregisters a network update system from all network update stages. /// + /// The implementation to deregister from all network updates public static void UnregisterAllNetworkUpdates(this INetworkUpdateSystem updateSystem) { foreach (NetworkUpdateStage updateStage in Enum.GetValues(typeof(NetworkUpdateStage))) @@ -105,6 +139,8 @@ namespace Unity.Netcode /// /// Unregisters a network update system from a specific network update stage. /// + /// The implementation to deregister from all network updates + /// The to be deregistered from the implementation public static void UnregisterNetworkUpdate(this INetworkUpdateSystem updateSystem, NetworkUpdateStage updateStage = NetworkUpdateStage.Update) { var sysSet = s_UpdateSystem_Sets[updateStage]; diff --git a/Runtime/Exceptions/InvalidParentException.cs b/Runtime/Exceptions/InvalidParentException.cs index 66a3679..c7ecbaa 100644 --- a/Runtime/Exceptions/InvalidParentException.cs +++ b/Runtime/Exceptions/InvalidParentException.cs @@ -7,8 +7,18 @@ namespace Unity.Netcode /// public class InvalidParentException : Exception { + /// + /// Constructor for + /// public InvalidParentException() { } + + /// + /// public InvalidParentException(string message) : base(message) { } + + /// + /// + /// public InvalidParentException(string message, Exception innerException) : base(message, innerException) { } } } diff --git a/Runtime/Exceptions/SpawnStateException.cs b/Runtime/Exceptions/SpawnStateException.cs index 712ddef..71c6391 100644 --- a/Runtime/Exceptions/SpawnStateException.cs +++ b/Runtime/Exceptions/SpawnStateException.cs @@ -26,8 +26,15 @@ namespace Unity.Netcode public SpawnStateException(string message, Exception inner) : base(message, inner) { } } + /// + /// Exception thrown when a specified network channel is invalid + /// public class InvalidChannelException : Exception { + /// + /// Constructs an InvalidChannelException with a message + /// + /// the message public InvalidChannelException(string message) : base(message) { } } } diff --git a/Runtime/Logging/NetworkLog.cs b/Runtime/Logging/NetworkLog.cs index ef4008d..b5c210b 100644 --- a/Runtime/Logging/NetworkLog.cs +++ b/Runtime/Logging/NetworkLog.cs @@ -14,8 +14,23 @@ namespace Unity.Netcode public static LogLevel CurrentLogLevel => NetworkManager.Singleton == null ? LogLevel.Normal : NetworkManager.Singleton.LogLevel; // internal logging + + /// + /// Locally logs a info log with Netcode prefixing. + /// + /// The message to log public static void LogInfo(string message) => Debug.Log($"[Netcode] {message}"); + + /// + /// Locally logs a warning log with Netcode prefixing. + /// + /// The message to log public static void LogWarning(string message) => Debug.LogWarning($"[Netcode] {message}"); + + /// + /// Locally logs a error log with Netcode prefixing. + /// + /// The message to log public static void LogError(string message) => Debug.LogError($"[Netcode] {message}"); /// diff --git a/Runtime/Messaging/CustomMessageManager.cs b/Runtime/Messaging/CustomMessageManager.cs index ed3afea..aa69ba7 100644 --- a/Runtime/Messaging/CustomMessageManager.cs +++ b/Runtime/Messaging/CustomMessageManager.cs @@ -193,6 +193,7 @@ namespace Unity.Netcode /// /// Sends a named message to all clients /// + /// The message name to send /// The message stream containing the data /// The delivery type (QoS) to send data with public void SendNamedMessageToAll(string messageName, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced) diff --git a/Runtime/Messaging/RpcParams.cs b/Runtime/Messaging/RpcParams.cs index 3ea839b..ed49a08 100644 --- a/Runtime/Messaging/RpcParams.cs +++ b/Runtime/Messaging/RpcParams.cs @@ -3,21 +3,52 @@ using Unity.Collections; namespace Unity.Netcode { + /// + /// Server-Side RPC + /// Place holder. + /// Note: Clients always send to one destination when sending RPCs to the server + /// so this structure is a place holder + /// public struct ServerRpcSendParams { } + /// + /// The receive parameters for server-side remote procedure calls + /// public struct ServerRpcReceiveParams { + /// + /// Server-Side RPC + /// The client identifier of the sender + /// public ulong SenderClientId; } + /// + /// Server-Side RPC + /// Can be used with any sever-side remote procedure call + /// Note: typically this is use primarily for the + /// public struct ServerRpcParams { + /// + /// The server RPC send parameters (currently a place holder) + /// public ServerRpcSendParams Send; + + /// + /// The client RPC receive parameters provides you with the sender's identifier + /// public ServerRpcReceiveParams Receive; } + /// + /// 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: + /// and + /// public struct ClientRpcSendParams { /// @@ -34,13 +65,32 @@ namespace Unity.Netcode public NativeArray? TargetClientIdsNativeArray; } + /// + /// Client-Side RPC + /// Place holder. + /// Note: Server will always be the sender, so this structure is a place holder + /// public struct ClientRpcReceiveParams { } + /// + /// 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). + /// + /// public struct ClientRpcParams { + /// + /// The client RPC send parameters provides you with the ability to send to a specific list of clients + /// public ClientRpcSendParams Send; + + /// + /// The client RPC receive parameters (currently a place holder) + /// public ClientRpcReceiveParams Receive; } diff --git a/Runtime/NetworkVariable/Collections/NetworkList.cs b/Runtime/NetworkVariable/Collections/NetworkList.cs index ca483fd..7fa8628 100644 --- a/Runtime/NetworkVariable/Collections/NetworkList.cs +++ b/Runtime/NetworkVariable/Collections/NetworkList.cs @@ -25,8 +25,15 @@ namespace Unity.Netcode /// public event OnListChangedDelegate OnListChanged; + /// + /// Constructor method for + /// public NetworkList() { } + /// + /// + /// + /// public NetworkList(IEnumerable values = default, NetworkVariableReadPermission readPerm = DefaultReadPerm, NetworkVariableWritePermission writePerm = DefaultWritePerm) @@ -448,6 +455,9 @@ namespace Unity.Netcode OnListChanged?.Invoke(listEvent); } + /// + /// This is actually unused left-over from a previous interface + /// public int LastModifiedTick { get @@ -457,6 +467,11 @@ namespace Unity.Netcode } } + /// + /// Overridden implementation. + /// CAUTION: If you derive from this class and override the method, + /// you **must** always invoke the base.Dispose() method! + /// public override void Dispose() { m_List.Dispose(); diff --git a/Runtime/NetworkVariable/NetworkVariable.cs b/Runtime/NetworkVariable/NetworkVariable.cs index ce43ab6..d32a80f 100644 --- a/Runtime/NetworkVariable/NetworkVariable.cs +++ b/Runtime/NetworkVariable/NetworkVariable.cs @@ -8,6 +8,7 @@ namespace Unity.Netcode /// /// A variable that can be synchronized over the network. /// + /// the unmanaged type for [Serializable] public class NetworkVariable : NetworkVariableBase where T : unmanaged { @@ -22,7 +23,12 @@ namespace Unity.Netcode /// public OnValueChangedDelegate OnValueChanged; - + /// + /// Constructor for + /// + /// initial value set that is of type T + /// the for this + /// the for this public NetworkVariable(T value = default, NetworkVariableReadPermission readPerm = DefaultReadPerm, NetworkVariableWritePermission writePerm = DefaultWritePerm) @@ -31,6 +37,9 @@ namespace Unity.Netcode m_InternalValue = value; } + /// + /// The internal value of the NetworkVariable + /// [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; } - + /// + /// Sets the , marks the dirty, and invokes the callback + /// if there are subscribers to that event. + /// + /// the new value of type `T` to be set/> private protected void Set(T value) { m_IsDirty = true; diff --git a/Runtime/NetworkVariable/NetworkVariableBase.cs b/Runtime/NetworkVariable/NetworkVariableBase.cs index 67c69d4..a35e31e 100644 --- a/Runtime/NetworkVariable/NetworkVariableBase.cs +++ b/Runtime/NetworkVariable/NetworkVariableBase.cs @@ -12,16 +12,36 @@ namespace Unity.Netcode /// internal const NetworkDelivery Delivery = NetworkDelivery.ReliableFragmentedSequenced; + /// + /// Maintains a link to the associated NetworkBehaviour + /// private protected NetworkBehaviour m_NetworkBehaviour; + /// + /// Initializes the NetworkVariable + /// + /// The NetworkBehaviour the NetworkVariable belongs to public void Initialize(NetworkBehaviour networkBehaviour) { m_NetworkBehaviour = networkBehaviour; } + /// + /// The default read permissions + /// public const NetworkVariableReadPermission DefaultReadPerm = NetworkVariableReadPermission.Everyone; + + /// + /// The default write permissions + /// public const NetworkVariableWritePermission DefaultWritePerm = NetworkVariableWritePermission.Server; + /// + /// The default constructor for that can be used to create a + /// custom NetworkVariable. + /// + /// the access settings + /// the access settings protected NetworkVariableBase( NetworkVariableReadPermission readPerm = DefaultReadPerm, NetworkVariableWritePermission writePerm = DefaultWritePerm) @@ -30,6 +50,10 @@ namespace Unity.Netcode WritePerm = writePerm; } + /// + /// The property is used to determine if the + /// value of the `NetworkVariable` has changed. + /// private protected bool m_IsDirty; /// @@ -43,11 +67,15 @@ namespace Unity.Netcode /// public readonly NetworkVariableReadPermission ReadPerm; + /// + /// The write permission for this var + /// public readonly NetworkVariableWritePermission WritePerm; /// /// Sets whether or not the variable needs to be delta synced /// + /// Whether or not the var is dirty public virtual void SetDirty(bool isDirty) { m_IsDirty = isDirty; @@ -70,6 +98,11 @@ namespace Unity.Netcode return m_IsDirty; } + /// + /// Gets if a specific client has permission to read the var or not + /// + /// The client id + /// Whether or not the client has permission to read public bool CanClientRead(ulong clientId) { switch (ReadPerm) @@ -82,6 +115,11 @@ namespace Unity.Netcode } } + /// + /// Gets if a specific client has permission to write the var or not + /// + /// The client id + /// Whether or not the client has permission to write public bool CanClientWrite(ulong clientId) { switch (WritePerm) @@ -127,6 +165,9 @@ namespace Unity.Netcode /// Whether or not the delta should be kept as dirty or consumed public abstract void ReadDelta(FastBufferReader reader, bool keepDirtyDelta); + /// + /// Virtual implementation + /// public virtual void Dispose() { } diff --git a/Runtime/NetworkVariable/NetworkVariablePermission.cs b/Runtime/NetworkVariable/NetworkVariablePermission.cs index bd9daa1..a067971 100644 --- a/Runtime/NetworkVariable/NetworkVariablePermission.cs +++ b/Runtime/NetworkVariable/NetworkVariablePermission.cs @@ -1,14 +1,32 @@ namespace Unity.Netcode { + /// + /// The permission types for reading a var + /// public enum NetworkVariableReadPermission { + /// + /// Everyone can read + /// Everyone, + /// + /// Only the owner and the server can read + /// Owner, } + /// + /// The permission types for writing a var + /// public enum NetworkVariableWritePermission { + /// + /// Only the server can write + /// Server, + /// + /// Only the owner can write + /// Owner } } diff --git a/Runtime/NetworkVariable/NetworkVariableSerialization.cs b/Runtime/NetworkVariable/NetworkVariableSerialization.cs index ead5345..cb3633f 100644 --- a/Runtime/NetworkVariable/NetworkVariableSerialization.cs +++ b/Runtime/NetworkVariable/NetworkVariableSerialization.cs @@ -120,10 +120,28 @@ namespace Unity.Netcode /// public class UserNetworkVariableSerialization { + /// + /// The write value delegate handler definition + /// + /// The to write the value of type `T` + /// The value of type `T` to be written public delegate void WriteValueDelegate(FastBufferWriter writer, in T value); + + /// + /// The read value delegate handler definition + /// + /// The to read the value of type `T` + /// The value of type `T` to be read public delegate void ReadValueDelegate(FastBufferReader reader, out T value); + /// + /// The delegate handler declaration + /// public static WriteValueDelegate WriteValue; + + /// + /// The delegate handler declaration + /// 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. /// + /// The type the associated NetworkVariable is templated on [Serializable] public static class NetworkVariableSerialization where T : unmanaged { diff --git a/Runtime/SceneManagement/NetworkSceneManager.cs b/Runtime/SceneManagement/NetworkSceneManager.cs index 3f6530f..7f7c9ff 100644 --- a/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/Runtime/SceneManagement/NetworkSceneManager.cs @@ -935,7 +935,7 @@ namespace Unity.Netcode /// Unloads an additively loaded scene. If you want to unload a mode loaded scene load another scene. /// When applicable, the is delivered within the via the /// - /// scene name to unload + /// /// ( means it was successful) public SceneEventProgressStatus UnloadScene(Scene scene) { @@ -1134,6 +1134,7 @@ namespace Unity.Netcode /// When applicable, the is delivered within the via /// /// the name of the scene to be loaded + /// how the scene will be loaded (single or additive mode) /// ( means it was successful) public SceneEventProgressStatus LoadScene(string sceneName, LoadSceneMode loadSceneMode) { diff --git a/Runtime/Serialization/BitCounter.cs b/Runtime/Serialization/BitCounter.cs index 4feb447..9657712 100644 --- a/Runtime/Serialization/BitCounter.cs +++ b/Runtime/Serialization/BitCounter.cs @@ -2,6 +2,9 @@ using System.Runtime.CompilerServices; namespace Unity.Netcode { + /// + /// Utility class to count the number of bytes or bits needed to serialize a value. + /// public static class BitCounter { // Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest) diff --git a/Runtime/Serialization/BufferSerializer.cs b/Runtime/Serialization/BufferSerializer.cs index 234eb9a..4f35888 100644 --- a/Runtime/Serialization/BufferSerializer.cs +++ b/Runtime/Serialization/BufferSerializer.cs @@ -62,80 +62,507 @@ namespace Unity.Netcode return m_Implementation.GetFastBufferWriter(); } + + /// + /// Read or write a string + /// + /// The value to read/write + /// If true, characters will be limited to one-byte ASCII characters public void SerializeValue(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValue(ref s, oneByteChars); + + /// + /// Read or write a single byte + /// + /// The value to read/write public void SerializeValue(ref byte value) => m_Implementation.SerializeValue(ref value); + + /// + /// 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. + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => m_Implementation.SerializeValue(ref value); + + /// + /// 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. + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an enum value + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of enum values + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a struct value implementing ISerializeByMemcpy + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of struct values implementing ISerializeByMemcpy + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a struct or class value implementing INetworkSerializable + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of struct or class values implementing INetworkSerializable + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized public void SerializeValue(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Vector2 value + /// + /// The value to read/write public void SerializeValue(ref Vector2 value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Vector2 values + /// + /// The values to read/write public void SerializeValue(ref Vector2[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Vector3 value + /// + /// The value to read/write public void SerializeValue(ref Vector3 value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Vector3 values + /// + /// The values to read/write public void SerializeValue(ref Vector3[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Vector2Int value + /// + /// The value to read/write public void SerializeValue(ref Vector2Int value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Vector2Int values + /// + /// The values to read/write public void SerializeValue(ref Vector2Int[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Vector3Int value + /// + /// The value to read/write public void SerializeValue(ref Vector3Int value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Vector3Int values + /// + /// The values to read/write public void SerializeValue(ref Vector3Int[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Vector4 value + /// + /// The value to read/write public void SerializeValue(ref Vector4 value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Vector4 values + /// + /// The values to read/write public void SerializeValue(ref Vector4[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Quaternion value + /// + /// The value to read/write public void SerializeValue(ref Quaternion value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Quaternion values + /// + /// The values to read/write public void SerializeValue(ref Quaternion[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Color value + /// + /// The value to read/write public void SerializeValue(ref Color value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Color values + /// + /// The values to read/write public void SerializeValue(ref Color[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Color32 value + /// + /// The value to read/write public void SerializeValue(ref Color32 value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Color32 values + /// + /// The values to read/write public void SerializeValue(ref Color32[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Ray value + /// + /// The value to read/write public void SerializeValue(ref Ray value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Ray values + /// + /// The values to read/write public void SerializeValue(ref Ray[] value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write a Ray2D value + /// + /// The value to read/write public void SerializeValue(ref Ray2D value) => m_Implementation.SerializeValue(ref value); + + /// + /// Read or write an array of Ray2D values + /// + /// The values to read/write public void SerializeValue(ref Ray2D[] value) => m_Implementation.SerializeValue(ref value); // There are many FixedString types, but all of them share the interfaces INativeList and IUTF8Bytes. // INativeList 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. + /// + /// Read or write a FixedString value + /// + /// The network serializable type + /// The values to read/write + /// An unused parameter used for enabling overload resolution of FixedStrings public void SerializeValue(ref T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes => m_Implementation.SerializeValue(ref value); + /// + /// Read or write a NetworkSerializable value. + /// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only. + /// + /// The network serializable type + /// The values to read/write public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new() => m_Implementation.SerializeNetworkSerializable(ref value); + /// + /// 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. + /// + /// + /// public bool PreCheck(int amount) { return m_Implementation.PreCheck(amount); } + /// + /// 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. + /// + /// The value to read/write + /// If true, characters will be limited to one-byte ASCII characters public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) => m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref byte value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable type + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints public void SerializeValuePreChecked(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable types in an array + /// The values to read/write + /// An unused parameter used for enabling overload resolution of primitives public void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable type + /// The values to read/write + /// An unused parameter used for enabling overload resolution of enums public void SerializeValuePreChecked(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable types in an array + /// The values to read/write + /// An unused parameter used for enabling overload resolution of enums public void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable type + /// The values to read/write + /// An unused parameter used for enabling overload resolution of structs public void SerializeValuePreChecked(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The network serializable types in an array + /// The values to read/write + /// An unused parameter used for enabling overload resolution of structs public void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector2 value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The values to read/write public void SerializeValuePreChecked(ref Vector2[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector3 value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The values to read/write public void SerializeValuePreChecked(ref Vector3[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector2Int value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The values to read/write public void SerializeValuePreChecked(ref Vector2Int[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector3Int value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector3Int[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector4 value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Vector4[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Quaternion value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Quaternion[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Color value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Color[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Color32 value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Color32[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Ray value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Ray[] value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Ray2D value) => m_Implementation.SerializeValuePreChecked(ref value); + + /// + /// 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. + /// + /// The value to read/write public void SerializeValuePreChecked(ref Ray2D[] value) => m_Implementation.SerializeValuePreChecked(ref value); // There are many FixedString types, but all of them share the interfaces INativeList 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. + + /// + /// 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. + /// + /// The network serializable type + /// The value to read/write + /// An unused parameter that can be used for enabling overload resolution for fixed strings public void SerializeValuePreChecked(ref T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes => m_Implementation.SerializeValuePreChecked(ref value); } diff --git a/Runtime/Serialization/BytePacker.cs b/Runtime/Serialization/BytePacker.cs index c017592..737faf7 100644 --- a/Runtime/Serialization/BytePacker.cs +++ b/Runtime/Serialization/BytePacker.cs @@ -6,6 +6,7 @@ namespace Unity.Netcode { /// /// Utility class for packing values in serialization. + /// to unpack packed values. /// public static class BytePacker { @@ -282,14 +283,49 @@ namespace Unity.Netcode public void WriteValueBitPacked(FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else + /// + /// Maximum serializable value for a BitPacked ushort (minimum for unsigned is 0) + /// public const ushort BitPackedUshortMax = (1 << 15) - 1; + + /// + /// Maximum serializable value for a BitPacked short + /// public const short BitPackedShortMax = (1 << 14) - 1; + + /// + /// Minimum serializable value size for a BitPacked ushort + /// public const short BitPackedShortMin = -(1 << 14); + + /// + /// Maximum serializable value for a BitPacked uint (minimum for unsigned is 0) + /// public const uint BitPackedUintMax = (1 << 30) - 1; + + /// + /// Maximum serializable value for a BitPacked int + /// public const int BitPackedIntMax = (1 << 29) - 1; + + /// + /// Minimum serializable value size for a BitPacked int + /// public const int BitPackedIntMin = -(1 << 29); + + /// + /// Maximum serializable value for a BitPacked ulong (minimum for unsigned is 0) + /// public const ulong BitPackedULongMax = (1L << 61) - 1; + + /// + /// Maximum serializable value for a BitPacked long + /// public const long BitPackedLongMax = (1L << 60) - 1; + + /// + /// Minimum serializable value size for a BitPacked long + /// public const long BitPackedLongMin = -(1L << 60); /// diff --git a/Runtime/Serialization/ByteUnpacker.cs b/Runtime/Serialization/ByteUnpacker.cs index 5cf2ace..a84a0d9 100644 --- a/Runtime/Serialization/ByteUnpacker.cs +++ b/Runtime/Serialization/ByteUnpacker.cs @@ -4,14 +4,26 @@ using UnityEngine; namespace Unity.Netcode { + /// + /// Byte Unpacking Helper Class + /// Use this class to unpack values during deserialization for values that were packed. + /// to pack unpacked values + /// public static class ByteUnpacker { #if UNITY_NETCODE_DEBUG_NO_PACKING - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValuePacked(FastBufferReader reader, out T value) where T: unmanaged => reader.ReadValueSafe(out value); #else + /// + /// Read a packed enum value + /// + /// The reader to read from + /// The value that's read + /// Type of enum to read + /// Throws InvalidOperationException if an enum somehow ends up not being the size of a byte, short, int, or long (which should be impossible) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void ReadValuePacked(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(FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value); #else diff --git a/Runtime/Serialization/FastBufferReader.cs b/Runtime/Serialization/FastBufferReader.cs index d5c6341..90ecd4a 100644 --- a/Runtime/Serialization/FastBufferReader.cs +++ b/Runtime/Serialization/FastBufferReader.cs @@ -6,6 +6,12 @@ using UnityEngine; namespace Unity.Netcode { + /// + /// Optimized class used for reading values from a byte stream + /// + /// + /// + /// public struct FastBufferReader : IDisposable { internal struct ReaderHandle @@ -87,15 +93,15 @@ namespace Unity.Netcode /// /// Create a FastBufferReader from a NativeArray. /// - /// A new buffer will be created using the given and the value will be copied in. + /// A new buffer will be created using the given and the value will be copied in. /// FastBufferReader will then own the data. /// - /// The exception to this is when the passed in is Allocator.None. In this scenario, + /// The exception to this is when the 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 is explicitly set + /// stored anywhere in heap memory). This is true, unless the 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. /// @@ -162,15 +168,15 @@ namespace Unity.Netcode /// /// Create a FastBufferReader from an existing byte buffer. /// - /// A new buffer will be created using the given and the value will be copied in. + /// A new buffer will be created using the given and the value will be copied in. /// FastBufferReader will then own the data. /// - /// The exception to this is when the passed in is Allocator.None. In this scenario, + /// The exception to this is when the 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 is explicitly set + /// stored anywhere in heap memory). This is true, unless the 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. /// @@ -187,15 +193,15 @@ namespace Unity.Netcode /// /// Create a FastBufferReader from a FastBufferWriter. /// - /// A new buffer will be created using the given and the value will be copied in. + /// A new buffer will be created using the given and the value will be copied in. /// FastBufferReader will then own the data. /// - /// The exception to this is when the passed in is Allocator.None. In this scenario, + /// The exception to this is when the 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 is explicitly set + /// stored anywhere in heap memory). This is true, unless the 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. /// @@ -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 and the value will be copied in. + /// A new buffer will be created using the given and the value will be copied in. /// FastBufferReader will then own the data. /// - /// The exception to this is when the passed in is Allocator.None. In this scenario, + /// The exception to this is when the 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 } /// - /// Frees the allocated buffer + /// implementation that frees the allocated buffer /// 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. /// + /// the type `T` of the value you are trying to read /// The value you want to read /// True if the read is allowed, false otherwise /// If called while in a bitwise context @@ -364,7 +371,7 @@ namespace Unity.Netcode /// Differs from TryBeginRead only in that it won't ever move the AllowedReadMark backward. /// /// - /// + /// true upon success /// [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!! /// - /// + /// byte array [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte[] ToArray() { @@ -408,7 +415,7 @@ namespace Unity.Netcode /// /// Gets a direct pointer to the underlying buffer /// - /// + /// pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { @@ -418,7 +425,7 @@ namespace Unity.Netcode /// /// Gets a direct pointer to the underlying buffer at the current read position /// - /// + /// pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { @@ -428,8 +435,8 @@ namespace Unity.Netcode /// /// Read an INetworkSerializable /// - /// INetworkSerializable instance /// + /// INetworkSerializable instance /// public void ReadNetworkSerializable(out T value) where T : INetworkSerializable, new() { @@ -442,7 +449,7 @@ namespace Unity.Netcode /// Read an array of INetworkSerializables /// /// INetworkSerializable instance - /// + /// the array to read the values of type `T` into /// public void ReadNetworkSerializable(out T[] value) where T : INetworkSerializable, new() { @@ -537,7 +544,7 @@ namespace Unity.Netcode /// Value to read /// Number of bytes /// Offset into the value to write the bytes - /// + /// the type value to read the value into /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -738,127 +745,522 @@ namespace Unity.Netcode } } + /// + /// Read a NetworkSerializable value + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value); + + /// + /// Read a NetworkSerializable array + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value); + /// + /// Read a NetworkSerializable value + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value); + + /// + /// Read a NetworkSerializable array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new() => ReadNetworkSerializable(out value); + + /// + /// Read a struct + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value); + + /// + /// Read a struct array + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanaged(out value); + /// + /// Read a struct + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value); + + /// + /// Read a struct array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => ReadUnmanagedSafe(out value); + /// + /// 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. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => ReadUnmanaged(out value); + + /// + /// 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. + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => ReadUnmanaged(out value); + /// + /// 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. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => ReadUnmanagedSafe(out value); + + /// + /// 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. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => ReadUnmanagedSafe(out value); + /// + /// Read an enum value + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value); + + /// + /// Read an enum array + /// + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanaged(out value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Read an enum value + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The value to read + /// An unused parameter used for enabling overload resolution based on generic constraints + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Read an enum array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// The type being serialized + /// The values to read + /// An unused parameter used for enabling overload resolution based on generic constraints + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum => ReadUnmanagedSafe(out value); + /// + /// Read a Vector2 + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector2 value) => ReadUnmanaged(out value); + + /// + /// Read a Vector2 array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector2[] value) => ReadUnmanaged(out value); + + /// + /// Read a Vector3 + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector3 value) => ReadUnmanaged(out value); + + /// + /// Read a Vector3 array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector3[] value) => ReadUnmanaged(out value); + + /// + /// Read a Vector2Int + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector2Int value) => ReadUnmanaged(out value); + + /// + /// Read a Vector2Int array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector2Int[] value) => ReadUnmanaged(out value); + + /// + /// Read a Vector3Int + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector3Int value) => ReadUnmanaged(out value); + + /// + /// Read a Vector3Int array + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector3Int[] value) => ReadUnmanaged(out value); + + /// + /// Read a Vector4 + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector4 value) => ReadUnmanaged(out value); + + /// + /// Read a Vector4 + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Vector4[] value) => ReadUnmanaged(out value); + + /// + /// Read a Quaternion + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Quaternion value) => ReadUnmanaged(out value); + + /// + /// Read a Quaternion array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Quaternion[] value) => ReadUnmanaged(out value); + + /// + /// Read a Color + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Color value) => ReadUnmanaged(out value); + + /// + /// Read a Color array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Color[] value) => ReadUnmanaged(out value); + + /// + /// Read a Color32 + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Color32 value) => ReadUnmanaged(out value); + + /// + /// Read a Color32 array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Color32[] value) => ReadUnmanaged(out value); + + /// + /// Read a Ray + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Ray value) => ReadUnmanaged(out value); + + /// + /// Read a Ray array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Ray[] value) => ReadUnmanaged(out value); + + /// + /// Read a Ray2D + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Ray2D value) => ReadUnmanaged(out value); + + /// + /// Read a Ray2D array + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValue(out Ray2D[] value) => ReadUnmanaged(out value); + + /// + /// Read a Vector2 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector2 value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector2 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector2[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector3 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector3 value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector3 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector3[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector2Int + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector2Int value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector2Int array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector2Int[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector3Int + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector3Int value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector3Int array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector3Int[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector4 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector4 value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Vector4 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Vector4[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Quaternion + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Quaternion value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Quaternion array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Quaternion[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Color + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Color value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Collor array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Color[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Color32 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Color32 value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Color32 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Color32[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Ray + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Ray value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Ray array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Ray[] value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Ray2D + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueSafe(out Ray2D value) => ReadUnmanagedSafe(out value); + + /// + /// Read a Ray2D array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the values to read [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. + + /// + /// 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) + /// + /// the value to read + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadValue(out T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes @@ -878,6 +1291,16 @@ namespace Unity.Netcode ReadBytes(value.GetUnsafePtr(), length); } + + /// + /// Read a FixedString value. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// the value to read + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadValueSafe(out T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes diff --git a/Runtime/Serialization/FastBufferWriter.cs b/Runtime/Serialization/FastBufferWriter.cs index f0f5407..6cb133f 100644 --- a/Runtime/Serialization/FastBufferWriter.cs +++ b/Runtime/Serialization/FastBufferWriter.cs @@ -6,6 +6,12 @@ using UnityEngine; namespace Unity.Netcode { + /// + /// Optimized class used for writing values into a byte stream + /// + /// + /// + /// public struct FastBufferWriter : IDisposable { internal struct WriterHandle @@ -108,7 +114,7 @@ namespace Unity.Netcode } /// - /// Frees the allocated buffer + /// implementation that frees the allocated buffer /// 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. /// - /// The value you want to write + /// The value type to write + /// The value of the type `T` you want to write /// True if the write is allowed, false otherwise /// If called while in a bitwise context [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. + /// + /// 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. + /// public struct ForPrimitives { } + /// + /// 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. + /// public struct ForEnums { } + /// + /// 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. + /// public struct ForStructs { } + /// + /// 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. + /// public struct ForNetworkSerializable { } + /// + /// 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. + /// public struct ForFixedStrings { } + /// + /// Write a NetworkSerializable value + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value); + + /// + /// Write a NetworkSerializable array + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value); + + /// + /// Write a NetworkSerializable value + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in T value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value); + + /// + /// Write a NetworkSerializable array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(T[] value, ForNetworkSerializable unused = default) where T : INetworkSerializable => WriteNetworkSerializable(value); + /// + /// Write a struct + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value); + + /// + /// Write a struct array + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanaged(value); + + /// + /// Write a struct + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in T value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value); + + /// + /// Write a struct array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(T[] value, ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy => WriteUnmanagedSafe(value); + /// + /// 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. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => WriteUnmanaged(value); + + /// + /// 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. + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => WriteUnmanaged(value); + + /// + /// 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. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in T value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => WriteUnmanagedSafe(value); + + /// + /// 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. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(T[] value, ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable => WriteUnmanagedSafe(value); + /// + /// Write an enum value + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value); + + /// + /// Write an enum array + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanaged(value); + + /// + /// Write an enum value + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in T value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value); + + /// + /// Write an enum array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The values to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(T[] value, ForEnums unused = default) where T : unmanaged, Enum => WriteUnmanagedSafe(value); + /// + /// Write a Vector2 + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Vector2 value) => WriteUnmanaged(value); + + /// + /// Write a Vector2 array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Vector2[] value) => WriteUnmanaged(value); + + /// + /// Write a Vector3 + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Vector3 value) => WriteUnmanaged(value); + + /// + /// Write a Vector3 array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Vector3[] value) => WriteUnmanaged(value); + + /// + /// Write a Vector2Int + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Vector2Int value) => WriteUnmanaged(value); + + /// + /// Write a Vector2Int array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Vector2Int[] value) => WriteUnmanaged(value); + + /// + /// Write a Vector3Int + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Vector3Int value) => WriteUnmanaged(value); + + /// + /// Write a Vector3Int array + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Vector3Int[] value) => WriteUnmanaged(value); + + /// + /// Write a Vector4 + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Vector4 value) => WriteUnmanaged(value); + + /// + /// Write a Vector4 + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Vector4[] value) => WriteUnmanaged(value); + + /// + /// Write a Quaternion + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Quaternion value) => WriteUnmanaged(value); + + /// + /// Write a Quaternion array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Quaternion[] value) => WriteUnmanaged(value); + + /// + /// Write a Color + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Color value) => WriteUnmanaged(value); + + /// + /// Write a Color array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Color[] value) => WriteUnmanaged(value); + + /// + /// Write a Color32 + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Color32 value) => WriteUnmanaged(value); + + /// + /// Write a Color32 array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Color32[] value) => WriteUnmanaged(value); + + /// + /// Write a Ray + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Ray value) => WriteUnmanaged(value); + + /// + /// Write a Ray array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Ray[] value) => WriteUnmanaged(value); + + /// + /// Write a Ray2D + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(in Ray2D value) => WriteUnmanaged(value); + + /// + /// Write a Ray2D array + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValue(Ray2D[] value) => WriteUnmanaged(value); + + /// + /// Write a Vector2 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Vector2 value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector2 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Vector2[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector3 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Vector3 value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector3 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Vector3[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector2Int + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Vector2Int value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector2Int array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Vector2Int[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector3Int + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Vector3Int value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector3Int array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Vector3Int[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector4 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Vector4 value) => WriteUnmanagedSafe(value); + + /// + /// Write a Vector4 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Vector4[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Quaternion + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Quaternion value) => WriteUnmanagedSafe(value); + + /// + /// Write a Quaternion array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Quaternion[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Color + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Color value) => WriteUnmanagedSafe(value); + + /// + /// Write a Collor array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Color[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Color32 + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Color32 value) => WriteUnmanagedSafe(value); + + /// + /// Write a Color32 array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Color32[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Ray + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Ray value) => WriteUnmanagedSafe(value); + + /// + /// Write a Ray array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(Ray[] value) => WriteUnmanagedSafe(value); + + /// + /// Write a Ray2D + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in Ray2D value) => WriteUnmanagedSafe(value); + + /// + /// Write a Ray2D array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// the values to write [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. + + /// + /// 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()) + /// + /// the value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteValue(in T value, ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes @@ -960,6 +1415,16 @@ namespace Unity.Netcode } } + + /// + /// 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. + /// + /// the value to write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueSafe(in T value, ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes diff --git a/Runtime/Serialization/ForceNetworkSerializeByMemcpy.cs b/Runtime/Serialization/ForceNetworkSerializeByMemcpy.cs index ca181b5..64cdf75 100644 --- a/Runtime/Serialization/ForceNetworkSerializeByMemcpy.cs +++ b/Runtime/Serialization/ForceNetworkSerializeByMemcpy.cs @@ -9,26 +9,58 @@ namespace Unity.Netcode /// public struct ForceNetworkSerializeByMemcpy : INetworkSerializeByMemcpy, IEquatable> where T : unmanaged, IEquatable { + /// + /// The wrapped value + /// public T Value; + /// + /// The default constructor for + /// + /// sets the initial value of type `T` public ForceNetworkSerializeByMemcpy(T value) { Value = value; } + /// + /// Convert implicitly from the ForceNetworkSerializeByMemcpy wrapper to the underlying value + /// + /// The wrapper + /// The underlying value public static implicit operator T(ForceNetworkSerializeByMemcpy container) => container.Value; + + /// + /// Convert implicitly from a T value to a ForceNetworkSerializeByMemcpy wrapper + /// + /// the value + /// a new wrapper public static implicit operator ForceNetworkSerializeByMemcpy(T underlyingValue) => new ForceNetworkSerializeByMemcpy { Value = underlyingValue }; + /// + /// Check if wrapped values are equal + /// + /// Other wrapper + /// true if equal public bool Equals(ForceNetworkSerializeByMemcpy other) { return Value.Equals(other.Value); } + /// + /// Check if this value is equal to a boxed object value + /// + /// The boxed value to check against + /// true if equal public override bool Equals(object obj) { return obj is ForceNetworkSerializeByMemcpy other && Equals(other); } + /// + /// Obtains the wrapped value's hash code + /// + /// Wrapped value's hash code public override int GetHashCode() { return Value.GetHashCode(); diff --git a/Runtime/Serialization/INetworkSerializeByMemcpy.cs b/Runtime/Serialization/INetworkSerializeByMemcpy.cs index c1996c4..8ec6df6 100644 --- a/Runtime/Serialization/INetworkSerializeByMemcpy.cs +++ b/Runtime/Serialization/INetworkSerializeByMemcpy.cs @@ -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`), it should be serialized via `INetworkSerializable` or via + /// like `NativeList<T>`), it should be serialized via `INetworkSerializable` or via /// `FastBufferReader`/`FastBufferWriter` extension methods. /// public interface INetworkSerializeByMemcpy diff --git a/Runtime/Serialization/IReaderWriter.cs b/Runtime/Serialization/IReaderWriter.cs index a567651..b469a61 100644 --- a/Runtime/Serialization/IReaderWriter.cs +++ b/Runtime/Serialization/IReaderWriter.cs @@ -4,81 +4,536 @@ using UnityEngine; namespace Unity.Netcode { + /// + /// Interface for an implementation of one side of a two-way serializer + /// public interface IReaderWriter { + /// + /// Check whether this implementation is a "reader" - if it's been constructed to deserialize data + /// bool IsReader { get; } + /// + /// Check whether this implementation is a "writer" - if it's been constructed to serialize data + /// bool IsWriter { get; } + /// + /// Get the underlying FastBufferReader struct. + /// Only valid when IsReader == true + /// + /// underlying FastBufferReader FastBufferReader GetFastBufferReader(); + /// + /// Get the underlying FastBufferWriter struct. + /// Only valid when IsWriter == true + /// + /// underlying FastBufferWriter FastBufferWriter GetFastBufferWriter(); + /// + /// Read or write a string + /// + /// The value to read/write + /// If true, characters will be limited to one-byte ASCII characters void SerializeValue(ref string s, bool oneByteChars = false); + + /// + /// Read or write a single byte + /// + /// The value to read/write void SerializeValue(ref byte value); + + /// + /// 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. + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable; + + /// + /// 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. + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable; + + /// + /// Read or write an enum value + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum; + + /// + /// Read or write an array of enum values + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum; + + /// + /// Read or write a struct value implementing ISerializeByMemcpy + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy; + + /// + /// Read or write an array of struct values implementing ISerializeByMemcpy + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy; + + /// + /// Read or write a struct or class value implementing INetworkSerializable + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new(); + + /// + /// Read or write an array of struct or class values implementing INetworkSerializable + /// + /// The values to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T[] value, FastBufferWriter.ForNetworkSerializable unused = default) where T : INetworkSerializable, new(); + + /// + /// Read or write a FixedString value + /// + /// The value to read/write + /// An unused parameter used for enabling overload resolution based on generic constraints + /// The type being serialized void SerializeValue(ref T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes; + + /// + /// Read or write a Vector2 value + /// + /// The value to read/write void SerializeValue(ref Vector2 value); + + /// + /// Read or write an array of Vector2 values + /// + /// The values to read/write void SerializeValue(ref Vector2[] value); + + /// + /// Read or write a Vector3 value + /// + /// The value to read/write void SerializeValue(ref Vector3 value); + + /// + /// Read or write an array of Vector3 values + /// + /// The values to read/write void SerializeValue(ref Vector3[] value); + + /// + /// Read or write a Vector2Int value + /// + /// The value to read/write void SerializeValue(ref Vector2Int value); + + /// + /// Read or write an array of Vector2Int values + /// + /// The values to read/write void SerializeValue(ref Vector2Int[] value); + + /// + /// Read or write a Vector3Int value + /// + /// The value to read/write void SerializeValue(ref Vector3Int value); + + /// + /// Read or write an array of Vector3Int values + /// + /// The values to read/write void SerializeValue(ref Vector3Int[] value); + + /// + /// Read or write a Vector4 value + /// + /// The value to read/write void SerializeValue(ref Vector4 value); + + /// + /// Read or write an array of Vector4 values + /// + /// The values to read/write void SerializeValue(ref Vector4[] value); + + /// + /// Read or write a Quaternion value + /// + /// The value to read/write void SerializeValue(ref Quaternion value); + + /// + /// Read or write an array of Quaternion values + /// + /// The values to read/write void SerializeValue(ref Quaternion[] value); + + /// + /// Read or write a Color value + /// + /// The value to read/write void SerializeValue(ref Color value); + + /// + /// Read or write an array of Color values + /// + /// The values to read/write void SerializeValue(ref Color[] value); + + /// + /// Read or write a Color32 value + /// + /// The value to read/write void SerializeValue(ref Color32 value); + + /// + /// Read or write an array of Color32 values + /// + /// The values to read/write void SerializeValue(ref Color32[] value); + + /// + /// Read or write a Ray value + /// + /// The value to read/write void SerializeValue(ref Ray value); + + /// + /// Read or write an array of Ray values + /// + /// The values to read/write void SerializeValue(ref Ray[] value); + + /// + /// Read or write a Ray2D value + /// + /// The value to read/write void SerializeValue(ref Ray2D value); + + /// + /// Read or write an array of Ray2D values + /// + /// The values to read/write void SerializeValue(ref Ray2D[] value); - // Has to have a different name to avoid conflicting with "where T: unmananged" + /// + /// Read or write a NetworkSerializable value. + /// SerializeValue() is the preferred method to do this - this is provided for backward compatibility only. + /// + /// The value to read/write + /// The network serializable type void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable, new(); + /// + /// 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. + /// + /// + /// bool PreCheck(int amount); + + /// + /// 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. + /// + /// The value to read/write + /// If true, characters will be limited to one-byte ASCII characters void SerializeValuePreChecked(ref string s, bool oneByteChars = false); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref byte value); + + /// + /// 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. + /// + /// The type being serialized + /// The value to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable; + + /// + /// 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. + /// + /// The type being serialized + /// The values to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForPrimitives unused = default) where T : unmanaged, IComparable, IConvertible, IComparable, IEquatable; + + /// + /// 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. + /// + /// The type being serialized + /// The value to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum; + + /// + /// 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. + /// + /// The type being serialized + /// The values to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForEnums unused = default) where T : unmanaged, Enum; + + /// + /// 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. + /// + /// The type being serialized + /// The value to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy; + + /// + /// 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. + /// + /// The type being serialized + /// The values to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T[] value, FastBufferWriter.ForStructs unused = default) where T : unmanaged, INetworkSerializeByMemcpy; + + /// + /// 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. + /// + /// The type being serialized + /// The value to read/write + /// An unused parameter that can be used for enabling overload resolution based on generic constraints void SerializeValuePreChecked(ref T value, FastBufferWriter.ForFixedStrings unused = default) where T : unmanaged, INativeList, IUTF8Bytes; + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector2 value); + + /// + /// 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. + /// + /// The values to read/write void SerializeValuePreChecked(ref Vector2[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector3 value); + + /// + /// 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. + /// + /// The values to read/write void SerializeValuePreChecked(ref Vector3[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector2Int value); + + /// + /// 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. + /// + /// The values to read/write void SerializeValuePreChecked(ref Vector2Int[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector3Int value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector3Int[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector4 value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Vector4[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Quaternion value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Quaternion[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Color value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Color[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Color32 value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Color32[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Ray value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Ray[] value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Ray2D value); + + /// + /// 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. + /// + /// The value to read/write void SerializeValuePreChecked(ref Ray2D[] value); } } diff --git a/Runtime/Serialization/NetworkBehaviourReference.cs b/Runtime/Serialization/NetworkBehaviourReference.cs index ee35127..6df91b8 100644 --- a/Runtime/Serialization/NetworkBehaviourReference.cs +++ b/Runtime/Serialization/NetworkBehaviourReference.cs @@ -96,8 +96,18 @@ namespace Unity.Netcode serializer.SerializeValue(ref m_NetworkBehaviourId); } + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// The this class is holding a reference to public static implicit operator NetworkBehaviour(NetworkBehaviourReference networkBehaviourRef) => GetInternal(networkBehaviourRef); + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// The created from the passed in as a parameter public static implicit operator NetworkBehaviourReference(NetworkBehaviour networkBehaviour) => new NetworkBehaviourReference(networkBehaviour); } } diff --git a/Runtime/Serialization/NetworkObjectReference.cs b/Runtime/Serialization/NetworkObjectReference.cs index 547e43f..11a2ab2 100644 --- a/Runtime/Serialization/NetworkObjectReference.cs +++ b/Runtime/Serialization/NetworkObjectReference.cs @@ -120,12 +120,32 @@ namespace Unity.Netcode serializer.SerializeValue(ref m_NetworkObjectId); } + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// The the is referencing public static implicit operator NetworkObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef); + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// The created from the parameter public static implicit operator NetworkObjectReference(NetworkObject networkObject) => new NetworkObjectReference(networkObject); + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// This returns the that the is attached to and is referenced by the passed in as a parameter public static implicit operator GameObject(NetworkObjectReference networkObjectRef) => Resolve(networkObjectRef).gameObject; + /// + /// Implicitly convert to . + /// + /// The to convert from. + /// The created from the parameter that has a component attached to it public static implicit operator NetworkObjectReference(GameObject gameObject) => new NetworkObjectReference(gameObject); } } diff --git a/Runtime/Spawning/NetworkSpawnManager.cs b/Runtime/Spawning/NetworkSpawnManager.cs index 9a39389..0a24169 100644 --- a/Runtime/Spawning/NetworkSpawnManager.cs +++ b/Runtime/Spawning/NetworkSpawnManager.cs @@ -126,6 +126,7 @@ namespace Unity.Netcode /// Returns a list of all NetworkObjects that belong to a client. /// /// the client's id + /// returns the list of s owned by the client public List GetClientOwnedObjects(ulong clientId) { if (!OwnershipToObjectsTable.ContainsKey(clientId)) @@ -172,9 +173,11 @@ namespace Unity.Netcode return GetPlayerNetworkObject(NetworkManager.LocalClientId); } + /// /// Returns the player object with a given clientId or null if one does not exist. This is only valid server side. /// + /// the client identifier of the player /// The player object with a given clientId or null if one does not exist public NetworkObject GetPlayerNetworkObject(ulong clientId) { diff --git a/Runtime/Timing/NetworkTickSystem.cs b/Runtime/Timing/NetworkTickSystem.cs index 103e185..5f2f209 100644 --- a/Runtime/Timing/NetworkTickSystem.cs +++ b/Runtime/Timing/NetworkTickSystem.cs @@ -3,6 +3,10 @@ using Unity.Profiling; namespace Unity.Netcode { + /// + /// Provides discretized time. + /// This is useful for games that require ticks happening at regular interval on the server and clients. + /// public class NetworkTickSystem { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -69,6 +73,8 @@ namespace Unity.Netcode /// /// Called after advancing the time system to run ticks based on the difference in time. /// + /// The local time in seconds + /// The server time in seconds public void UpdateTick(double localTimeSec, double serverTimeSec) { // store old local tick to know how many fixed ticks passed diff --git a/Runtime/Timing/NetworkTime.cs b/Runtime/Timing/NetworkTime.cs index 0a6cca9..9f86de9 100644 --- a/Runtime/Timing/NetworkTime.cs +++ b/Runtime/Timing/NetworkTime.cs @@ -108,6 +108,11 @@ namespace Unity.Netcode return new NetworkTime(m_TickRate, m_CachedTick); } + /// + /// Returns the time a number of ticks in the past. + /// + /// The number of ticks ago we're querying the time + /// public NetworkTime TimeTicksAgo(int ticks) { return this - new NetworkTime(TickRate, ticks); @@ -132,16 +137,34 @@ namespace Unity.Netcode } } + /// + /// Computes the time difference between two ticks + /// + /// End time + /// Start time + /// The time difference between start and end public static NetworkTime operator -(NetworkTime a, NetworkTime b) { return new NetworkTime(a.TickRate, a.Time - b.Time); } + /// + /// Computes the sum of two times + /// + /// First time + /// Second time + /// The sum of the two times passed in public static NetworkTime operator +(NetworkTime a, NetworkTime b) { return new NetworkTime(a.TickRate, a.Time + b.Time); } + /// + /// Computes the time a number of seconds later + /// + /// The start time + /// The number of seconds to add + /// The resulting time public static NetworkTime operator +(NetworkTime a, double b) { a.m_TimeSec += b; @@ -149,6 +172,12 @@ namespace Unity.Netcode return a; } + /// + /// Computes the time a number of seconds before + /// + /// The start time + /// The number of seconds to remove + /// The resulting time public static NetworkTime operator -(NetworkTime a, double b) { return a + -b; diff --git a/Runtime/Timing/NetworkTimeSystem.cs b/Runtime/Timing/NetworkTimeSystem.cs index 25c7766..e9de73a 100644 --- a/Runtime/Timing/NetworkTimeSystem.cs +++ b/Runtime/Timing/NetworkTimeSystem.cs @@ -36,12 +36,27 @@ namespace Unity.Netcode /// Gets or sets the ratio at which the NetworkTimeSystem speeds up or slows down time. /// public double AdjustmentRatio { get; set; } + + /// + /// The current local time with the local time offset applied + /// public double LocalTime => m_TimeSec + m_CurrentLocalTimeOffset; + + /// + /// The current server time with the server time offset applied + /// public double ServerTime => m_TimeSec + m_CurrentServerTimeOffset; internal double LastSyncedServerTimeSec { get; private set; } internal double LastSyncedRttSec { get; private set; } + /// + /// The constructor class for + /// + /// The amount of time, in seconds, the server should buffer incoming client messages. + /// The amount of the time in seconds the client should buffer incoming messages from the server. + /// The threshold, in seconds, used to force a hard catchup of network time. + /// The ratio at which the NetworkTimeSystem speeds up or slows down time. public NetworkTimeSystem(double localBufferSec, double serverBufferSec, double hardResetThresholdSec, double adjustmentRatio = 0.01d) { LocalBufferSec = localBufferSec; diff --git a/Runtime/Transports/NetworkTransport.cs b/Runtime/Transports/NetworkTransport.cs index 08783e3..9de70af 100644 --- a/Runtime/Transports/NetworkTransport.cs +++ b/Runtime/Transports/NetworkTransport.cs @@ -3,6 +3,11 @@ using UnityEngine; namespace Unity.Netcode { + /// + /// The generic transport class all Netcode for GameObjects network transport implementations + /// derive from. Use this class to add a custom transport. + /// for an example of how a transport is integrated + /// public abstract class NetworkTransport : MonoBehaviour { /// @@ -45,7 +50,7 @@ namespace Unity.Netcode } /// - /// Send a payload to the specified clientId, data and channelName. + /// Send a payload to the specified clientId, data and networkDelivery. /// /// The clientId to send to /// The data to send @@ -64,11 +69,13 @@ namespace Unity.Netcode /// /// Connects client to the server /// + /// Returns success or failure public abstract bool StartClient(); /// /// Starts to listening for incoming clients /// + /// Returns success or failure public abstract bool StartServer(); /// diff --git a/Runtime/Transports/UTP/NetworkMetricsContext.cs b/Runtime/Transports/UTP/NetworkMetricsContext.cs index c871f63..f55bd04 100644 --- a/Runtime/Transports/UTP/NetworkMetricsContext.cs +++ b/Runtime/Transports/UTP/NetworkMetricsContext.cs @@ -1,8 +1,17 @@ namespace Unity.Netcode.Transports.UTP { + /// + /// Caching structure to track network metrics related information. + /// public struct NetworkMetricsContext { + /// + /// The number of packet sent. + /// public uint PacketSentCount; + /// + /// The number of packet received. + /// public uint PacketReceivedCount; } } diff --git a/Runtime/Transports/UTP/UnityTransport.cs b/Runtime/Transports/UTP/UnityTransport.cs index 374eda0..ad2787f 100644 --- a/Runtime/Transports/UTP/UnityTransport.cs +++ b/Runtime/Transports/UTP/UnityTransport.cs @@ -16,6 +16,14 @@ namespace Unity.Netcode.Transports.UTP /// public interface INetworkStreamDriverConstructor { + /// + /// Creates the internal NetworkDriver + /// + /// The owner transport + /// The driver + /// The UnreliableFragmented NetworkPipeline + /// The UnreliableSequencedFragmented NetworkPipeline + /// The ReliableSequenced NetworkPipeline void CreateDriver( UnityTransport transport, out NetworkDriver driver, @@ -24,6 +32,9 @@ namespace Unity.Netcode.Transports.UTP out NetworkPipeline reliableSequencedPipeline); } + /// + /// Helper utility class to convert error codes to human readable error messages. + /// 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."; + /// + /// Convert error code to human readable error message. + /// + /// Status code of the error + /// Subject connection ID of the error + /// Human readable error message. public static string ErrorToString(Networking.Transport.Error.StatusCode error, ulong connectionId) { switch (error) @@ -67,11 +84,24 @@ namespace Unity.Netcode.Transports.UTP } } + /// + /// The Netcode for GameObjects NetworkTransport for UnityTransport. + /// Note: This is highly recommended to use over UNet. + /// public partial class UnityTransport : NetworkTransport, INetworkStreamDriverConstructor { + /// + /// Enum type stating the type of protocol + /// public enum ProtocolType { + /// + /// Unity Transport Protocol + /// UnityTransport, + /// + /// Unity Transport Protocol over Relay + /// RelayUnityTransport, } @@ -82,15 +112,34 @@ namespace Unity.Netcode.Transports.UTP Connected, } + /// + /// The default maximum (receive) packet queue size + /// public const int InitialMaxPacketQueueSize = 128; + + /// + /// The default maximum payload size + /// public const int InitialMaxPayloadSize = 6 * 1024; + + /// + /// The default maximum send queue size + /// 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 + + /// + /// The global implementation + /// public static INetworkStreamDriverConstructor s_DriverConstructor; #pragma warning restore IDE1006 // Naming Styles + + /// + /// Returns either the global implementation or the current instance + /// 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; } + /// + /// Structure to store the address to connect to + /// [Serializable] public struct ConnectionAddressData { + /// + /// IP address of the server (address to which clients will connect to). + /// [Tooltip("IP address of the server (address to which clients will connect to).")] [SerializeField] public string Address; + /// + /// UDP port of the server. + /// [Tooltip("UDP port of the server.")] [SerializeField] public ushort Port; + /// + /// IP address the server will listen on. If not provided, will use 'Address'. + /// [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; } + /// + /// Endpoint (IP address and port) clients will connect to. + /// public NetworkEndPoint ServerEndPoint => ParseNetworkEndpoint(Address, Port); + /// + /// Endpoint (IP address and port) server will listen/bind on. + /// public NetworkEndPoint ListenEndPoint => ParseNetworkEndpoint((ServerListenAddress == string.Empty) ? Address : ServerListenAddress, Port); } + /// + /// The connection (address) data for this instance. + /// This is where you can change IP Address, Port, or server's listen address. + /// + /// public ConnectionAddressData ConnectionData = s_DefaultConnectionAddressData; + /// + /// Parameters for the Network Simulator + /// [Serializable] public struct SimulatorParameters { + /// + /// 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. + /// [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; + /// + /// 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. + /// [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; + /// + /// Percentage of sent and received packets to drop. Only applies in the editor and in the editor and in developments builds. + /// [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; } + /// + /// 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) + /// public SimulatorParameters DebugSimulator = new SimulatorParameters { PacketDelayMS = 0, @@ -261,8 +351,14 @@ namespace Unity.Netcode.Transports.UTP private NetworkPipeline m_UnreliableSequencedFragmentedPipeline; private NetworkPipeline m_ReliableSequencedPipeline; + /// + /// The client id used to represent the server. + /// public override ulong ServerClientId => m_ServerClientId; + /// + /// The current ProtocolType used by the transport + /// public ProtocolType Protocol => m_ProtocolType; private RelayServerData m_RelayServerData; @@ -428,6 +524,14 @@ namespace Unity.Netcode.Transports.UTP m_ProtocolType = inProtocol; } + /// Set the relay server data for the server. + /// IP address of the relay server. + /// UDP port of the relay server. + /// Allocation ID as a byte array. + /// Allocation key as a byte array. + /// Connection data as a byte array. + /// The HostConnectionData as a byte array. + /// Whether the connection is secure (uses DTLS). 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 /// /// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call /// + /// The remote IP address + /// The remote port + /// The local listen address public void SetConnectionData(string ipv4Address, ushort port, string listenAddress = null) { ConnectionData = new ConnectionAddressData @@ -504,6 +611,8 @@ namespace Unity.Netcode.Transports.UTP /// /// Sets IP and Port information. This will be ignored if using the Unity Relay and you should call /// + /// The remote end point + /// The local listen endpoint public void SetConnectionData(NetworkEndPoint endPoint, NetworkEndPoint listenEndPoint = default) { string serverAddress = endPoint.Address.Split(':')[0]; @@ -916,6 +1025,9 @@ namespace Unity.Netcode.Transports.UTP } } + /// + /// Disconnects the local client from the remote + /// public override void DisconnectLocalClient() { if (m_State == State.Connected) @@ -940,6 +1052,10 @@ namespace Unity.Netcode.Transports.UTP } } + /// + /// Disconnects a remote client from the server + /// + /// The client to disconnect 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 } } + /// + /// Gets the current RTT for a specific client + /// + /// The client RTT to get + /// The RTT 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)); } + /// + /// Initializes the transport + /// + /// The NetworkManager that initialized and owns the transport public override void Initialize(NetworkManager networkManager = null) { Debug.Assert(sizeof(ulong) == UnsafeUtility.SizeOf(), "Netcode connection id size does not match UTP connection id size"); @@ -1000,6 +1125,13 @@ namespace Unity.Netcode.Transports.UTP #endif } + /// + /// Polls for incoming events, with an extra output parameter to report the precise time the event was received. + /// + /// The clientId this event is for + /// The incoming data payload + /// The time the event was received, as reported by Time.realtimeSinceStartup. + /// Returns the event type public override NetcodeNetworkEvent PollEvent(out ulong clientId, out ArraySegment payload, out float receiveTime) { clientId = default; @@ -1008,6 +1140,12 @@ namespace Unity.Netcode.Transports.UTP return NetcodeNetworkEvent.Nothing; } + /// + /// Send a payload to the specified clientId, data and networkDelivery. + /// + /// The clientId to send to + /// The data to send + /// The delivery type (QoS) to send data with public override void Send(ulong clientId, ArraySegment payload, NetworkDelivery networkDelivery) { if (payload.Count > m_MaxPayloadSize) @@ -1069,6 +1207,14 @@ namespace Unity.Netcode.Transports.UTP } } + /// + /// 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 + /// + /// true if the client was started and false if it failed to start the client public override bool StartClient() { if (m_Driver.IsCreated) @@ -1084,6 +1230,14 @@ namespace Unity.Netcode.Transports.UTP return succeeded; } + /// + /// 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 + /// + /// true if the server was started and false if it failed to start the server public override bool StartServer() { if (m_Driver.IsCreated) @@ -1113,6 +1267,9 @@ namespace Unity.Netcode.Transports.UTP } } + /// + /// Shuts down the transport + /// public override void Shutdown() { if (!m_Driver.IsCreated) @@ -1152,6 +1309,14 @@ namespace Unity.Netcode.Transports.UTP ); } + /// + /// Creates the internal NetworkDriver + /// + /// The owner transport + /// The driver + /// The UnreliableFragmented NetworkPipeline + /// The UnreliableSequencedFragmented NetworkPipeline + /// The ReliableSequenced NetworkPipeline public void CreateDriver(UnityTransport transport, out NetworkDriver driver, out NetworkPipeline unreliableFragmentedPipeline, out NetworkPipeline unreliableSequencedFragmentedPipeline, diff --git a/package.json b/package.json index 6c41a9f..70e4ac7 100644 --- a/package.json +++ b/package.json @@ -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": [ {