diff --git a/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef
index 9aa9b97a..22a5b611 100644
--- a/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef
+++ b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef
@@ -4,7 +4,10 @@
"references": [
"FishNet.Runtime",
"FishNet.Codegen.Cecil",
- "GameKit.Dependencies"
+ "GameKit.Dependencies",
+ "Unity.Burst",
+ "Unity.Mathematics",
+ "Unity.Collections"
],
"includePlatforms": [
"Editor"
diff --git a/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs
index 4eae338d..bfa4f8a2 100644
--- a/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs
+++ b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs
@@ -30,7 +30,7 @@ public class NotSerializerAttribute : Attribute { }
///
/// Method or type will be made public by codegen.
///
- internal class MakePublicAttribute : Attribute { }
+ public class MakePublicAttribute : Attribute { }
///
/// Method is a comparer for a value type.
diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs
index 43c45e12..0c63fa69 100644
--- a/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs
+++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs
@@ -43,10 +43,10 @@ public string GetAddress()
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ public void Kick(KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
if (CanKick())
- NetworkManager.ServerManager.Kick(this, kickReason, loggingType, log);
+ NetworkManager.ServerManager.Kick(this, kickReason, loggingType, log, immediately);
}
///
@@ -56,10 +56,10 @@ public void Kick(KickReason kickReason, LoggingType loggingType = LoggingType.Co
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ public void Kick(Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
if (CanKick())
- NetworkManager.ServerManager.Kick(this, reader, kickReason, loggingType, log);
+ NetworkManager.ServerManager.Kick(this, reader, kickReason, loggingType, log, immediately);
}
private bool CanKick()
diff --git a/Assets/FishNet/Runtime/FishNet.Runtime.asmdef b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef
index dc41020c..b2c5f6a9 100644
--- a/Assets/FishNet/Runtime/FishNet.Runtime.asmdef
+++ b/Assets/FishNet/Runtime/FishNet.Runtime.asmdef
@@ -4,7 +4,9 @@
"references": [
"GUID:894a6cc6ed5cd2645bb542978cbed6a9",
"GUID:1d82bdf40e2465b44b34adf79595e74c",
- "GUID:d8b63aba1907145bea998dd612889d6b"
+ "GUID:d8b63aba1907145bea998dd612889d6b",
+ "GUID:2665a8d13d1b3f18800f46e256720795",
+ "GUID:e0cd26848372d4e5c891c569017e11f1"
],
"includePlatforms": [],
"excludePlatforms": [],
diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs
index 4c312763..1bd718e9 100644
--- a/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs
+++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs
@@ -15,8 +15,9 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using FishNet.Managing;
-using Unity.Profiling;
using UnityEngine;
+using UnityEngine.Profiling;
+using Unity.Profiling;
using TimeManagerCls = FishNet.Managing.Timing.TimeManager;
namespace FishNet.Component.Animating
@@ -33,6 +34,7 @@ private struct ReceivedServerData
///
/// Gets an Arraysegment of received data.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ArraySegment GetArraySegment() => new(_data, 0, _length);
///
@@ -51,6 +53,7 @@ public ReceivedServerData(ArraySegment segment)
Buffer.BlockCopy(segment.Array, segment.Offset, _data, 0, _length);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (_data != null)
@@ -210,6 +213,7 @@ public void GetBuffer(int index, ref byte[] buffer, ref int length)
///
/// Resets buffers.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
BufferCount = 0;
@@ -274,6 +278,23 @@ public ParameterDetail(AnimatorControllerParameter controllerParameter, byte typ
}
#endregion
+ #region Private
+
+ #region Private Profiler Markers
+
+ private static readonly ProfilerMarker _pm_OnPreTick = new ProfilerMarker("NetworkAnimator.TimeManager_OnPreTick()");
+ private static readonly ProfilerMarker _pm_OnPostTick = new ProfilerMarker("NetworkAnimator.TimeManager_OnPostTick()");
+ private static readonly ProfilerMarker _pm_OnUpdate = new ProfilerMarker("NetworkAnimator.TimeManager_OnUpdate()");
+ private static readonly ProfilerMarker _pm_CheckSendToServer = new ProfilerMarker("NetworkAnimator.CheckSendToServer()");
+ private static readonly ProfilerMarker _pm_CheckSendToClients = new ProfilerMarker("NetworkAnimator.CheckSendToClients()");
+ private static readonly ProfilerMarker _pm_SmoothFloats = new ProfilerMarker("NetworkAnimator.SmoothFloats()");
+ private static readonly ProfilerMarker _pm_AnimatorUpdated = new ProfilerMarker("NetworkAnimator.AnimatorUpdated(ref ArraySegment, bool)");
+ private static readonly ProfilerMarker _pm_ApplyParametersUpdated = new ProfilerMarker("NetworkAnimator.ApplyParametersUpdated(ref ArraySegment)");
+
+ #endregion
+
+ #endregion
+
#region Public.
///
/// Parameters which will not be synchronized.
@@ -304,6 +325,14 @@ public Animator Animator
[SerializeField]
private bool _synchronizeWhenDisabled;
///
+ /// True to synchronize changes even when the animator component is disabled.
+ ///
+ public bool SynchronizeWhenDisabled
+ {
+ get { return _synchronizeWhenDisabled; }
+ set { _synchronizeWhenDisabled = value; }
+ }
+ ///
/// True to smooth float value changes for spectators.
///
[Tooltip("True to smooth float value changes for spectators.")]
@@ -334,6 +363,11 @@ public bool ClientAuthoritative
[Tooltip("True to synchronize server results back to owner. Typically used when you are changing animations on the server and are relying on the server response to update the clients animations.")]
[SerializeField]
private bool _sendToOwner;
+ ///
+ /// True to synchronize server results back to owner. Typically used when you are changing animations on the server and are relying on the server response to update the clients animations.
+ ///
+ public bool SendToOwner => _sendToOwner;
+
#endregion
#region Private.
@@ -370,12 +404,19 @@ public bool ClientAuthoritative
// ///
// private List _toClientsBuffer = new();
///
+ /// Synchronization enabled state. True by default
+ ///
+ private bool _isSynchronizationEnabled = true;
+ ///
/// Returns if the animator is exist and can be synchronized.
///
private bool _canSynchronizeAnimator
{
get
{
+ if (!_isSynchronizationEnabled)
+ return false;
+
if (!_isAnimatorSet)
return false;
@@ -459,17 +500,6 @@ private bool _canSmoothFloats
private bool _subscribedToTicks;
#endregion
- #region Private Profiler Markers
- private static readonly ProfilerMarker _pm_OnPreTick = new("NetworkAnimator.TimeManager_OnPreTick()");
- private static readonly ProfilerMarker _pm_OnPostTick = new("NetworkAnimator.TimeManager_OnPostTick()");
- private static readonly ProfilerMarker _pm_OnUpdate = new("NetworkAnimator.TimeManager_OnUpdate()");
- private static readonly ProfilerMarker _pm_CheckSendToServer = new("NetworkAnimator.CheckSendToServer()");
- private static readonly ProfilerMarker _pm_CheckSendToClients = new("NetworkAnimator.CheckSendToClients()");
- private static readonly ProfilerMarker _pm_SmoothFloats = new("NetworkAnimator.SmoothFloats()");
- private static readonly ProfilerMarker _pm_AnimatorUpdated = new("NetworkAnimator.AnimatorUpdated(ref ArraySegment, bool)");
- private static readonly ProfilerMarker _pm_ApplyParametersUpdated = new("NetworkAnimator.ApplyParametersUpdated(ref ArraySegment)");
- #endregion
-
#region Const.
/////
///// How much time to fall behind when using smoothing. Only increase value if the smoothing is sometimes jittery. Recommended values are between 0 and 0.04.
@@ -515,6 +545,7 @@ public override void OnSpawnServer(NetworkConnection connection)
public override void OnStartNetwork()
{
ChangeTickSubscription(true);
+ _isSynchronizationEnabled = true;
}
[APIExclude]
@@ -584,6 +615,7 @@ private void TimeManager_OnPreTick()
_fromServerBuffer.Clear();
return;
}
+
//Disabled/cannot start.
if (_startTick == 0)
return;
@@ -593,6 +625,7 @@ private void TimeManager_OnPreTick()
_startTick = 0;
return;
}
+
//Not enough time has passed to start queue.
if (TimeManager.LocalTick < _startTick)
return;
@@ -645,7 +678,7 @@ private void InitializeOnce()
//Don't run the rest if not in play mode.
if (!ApplicationState.IsPlaying())
return;
-
+
if (!_canSynchronizeAnimator)
{
//Debug.LogWarning("Animator is null or not enabled; unable to initialize for animator. Use SetAnimator if animator was changed or enable the animator.");
@@ -708,6 +741,15 @@ private void InitializeOnce()
}
}
}
+
+ ///
+ /// Sets synchronization state to NetworkAnimator. Enabled by default.
+ ///
+ ///
+ public void SetSynchronizationState(bool state)
+ {
+ _isSynchronizationEnabled = state;
+ }
///
/// Sets which animator to use. You must call this with the appropriate animator on all clients and server. This change is not automatically synchronized.
@@ -846,6 +888,7 @@ private void CheckSendToClients()
SendSegment(new(buffer, 0, bufferLength));
}
+
//Reset client auth buffer.
_clientAuthoritativeUpdates.Reset();
}
@@ -978,6 +1021,7 @@ private bool AnimatorUpdated(out ArraySegment updatedBytes, bool forceAll
_writer.WriteUInt8Unpacked(_triggerUpdates[i].ParameterIndex);
_writer.WriteBoolean(_triggerUpdates[i].Setting);
}
+
_triggerUpdates.Clear();
/* States. */
@@ -1069,7 +1113,7 @@ private bool AnimatorUpdated(out ArraySegment updatedBytes, bool forceAll
//Nothing to update.
if (_writer.Position == 0)
return false;
-
+
updatedBytes = _writer.GetArraySegment();
return true;
}
@@ -1225,6 +1269,7 @@ private bool ReturnCurrentLayerState(out int stateHash, out float normalizedTime
/// Immediately sends all variables and states of layers.
/// This is a very bandwidth intensive operation.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SendAll()
{
_forceAllOnTimed = true;
@@ -1234,6 +1279,7 @@ public void SendAll()
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Play(string name)
{
Play(Animator.StringToHash(name));
@@ -1242,6 +1288,7 @@ public void Play(string name)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Play(int hash)
{
for (int i = 0; i < _animator.layerCount; i++)
@@ -1251,6 +1298,7 @@ public void Play(int hash)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Play(string name, int layer)
{
Play(Animator.StringToHash(name), layer);
@@ -1259,6 +1307,7 @@ public void Play(string name, int layer)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Play(int hash, int layer)
{
Play(hash, layer, 0f);
@@ -1267,6 +1316,7 @@ public void Play(int hash, int layer)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Play(string name, int layer, float normalizedTime)
{
Play(Animator.StringToHash(name), layer, normalizedTime);
@@ -1289,6 +1339,7 @@ public void Play(int hash, int layer, float normalizedTime)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PlayInFixedTime(string name, float fixedTime)
{
PlayInFixedTime(Animator.StringToHash(name), fixedTime);
@@ -1297,6 +1348,7 @@ public void PlayInFixedTime(string name, float fixedTime)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PlayInFixedTime(int hash, float fixedTime)
{
for (int i = 0; i < _animator.layerCount; i++)
@@ -1306,6 +1358,7 @@ public void PlayInFixedTime(int hash, float fixedTime)
///
/// Plays a state.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PlayInFixedTime(string name, int layer, float fixedTime)
{
PlayInFixedTime(Animator.StringToHash(name), layer, fixedTime);
@@ -1335,6 +1388,7 @@ public void PlayInFixedTime(int hash, int layer, float fixedTime)
///
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CrossFade(string stateName, float normalizedTransitionDuration, int layer, float normalizedTimeOffset = float.NegativeInfinity, float normalizedTransitionTime = 0.0f)
{
CrossFade(Animator.StringToHash(stateName), normalizedTransitionDuration, layer, normalizedTimeOffset, normalizedTransitionTime);
@@ -1367,6 +1421,7 @@ public void CrossFade(int hash, float normalizedTransitionDuration, int layer, f
///
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CrossFadeInFixedTime(string stateName, float fixedTransitionDuration, int layer, float fixedTimeOffset = 0.0f, float normalizedTransitionTime = 0.0f)
{
CrossFadeInFixedTime(Animator.StringToHash(stateName), fixedTransitionDuration, layer, fixedTimeOffset, normalizedTransitionTime);
@@ -1397,6 +1452,7 @@ public void CrossFadeInFixedTime(int hash, float fixedTransitionDuration, int la
/// Sets a trigger on the animator and sends it over the network.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetTrigger(int hash)
{
if (!_canSynchronizeAnimator)
@@ -1408,6 +1464,7 @@ public void SetTrigger(int hash)
/// Sets a trigger on the animator and sends it over the network.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetTrigger(string name)
{
SetTrigger(Animator.StringToHash(name));
@@ -1417,6 +1474,7 @@ public void SetTrigger(string name)
/// Resets a trigger on the animator and sends it over the network.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetTrigger(int hash)
{
UpdateTrigger(hash, false);
@@ -1426,6 +1484,7 @@ public void ResetTrigger(int hash)
/// Resets a trigger on the animator and sends it over the network.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetTrigger(string name)
{
ResetTrigger(Animator.StringToHash(name));
diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs
index b44b8579..f9789010 100644
--- a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs
+++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs
@@ -12,9 +12,10 @@
using GameKit.Dependencies.Utilities;
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
using FishNet.Managing.Timing;
-using Unity.Profiling;
using UnityEngine;
+using Unity.Profiling;
using UnityEngine.Scripting;
using static FishNet.Object.NetworkObject;
@@ -74,12 +75,14 @@ public void Update(ArraySegment data, Channel channel, bool updateHasData,
///
/// Will cause this data to send on the reliable channel once even if data is unchanged.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SendReliably()
{
HasData = true;
Channel = Channel.Reliable;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetState()
{
HasData = false;
@@ -159,6 +162,7 @@ public class GoalData : IResettable
[Preserve]
public GoalData() { }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetState()
{
ReceivedTick = 0;
@@ -166,6 +170,7 @@ public void ResetState()
Rates.ResetState();
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InitializeState() { }
}
@@ -200,6 +205,7 @@ public class RateData : IResettable
[Preserve]
public RateData() { }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(RateData rd)
{
Update(rd.Position, rd.Rotation, rd.Scale, rd.LastUnalteredPositionRate, rd.TickSpan, rd.TimeRemaining);
@@ -208,6 +214,7 @@ public void Update(RateData rd)
///
/// Updates rates.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(float position, float rotation, float scale, float unalteredPositionRate, uint tickSpan, float timeRemaining)
{
Position = position;
@@ -218,6 +225,7 @@ public void Update(float position, float rotation, float scale, float unalteredP
TimeRemaining = timeRemaining;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetState()
{
Position = 0f;
@@ -228,6 +236,7 @@ public void ResetState()
TimeRemaining = 0f;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InitializeState() { }
}
@@ -282,13 +291,16 @@ public enum ExtrapolateState : byte
[Preserve]
public TransformData() { }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetIsDefaultToFalse() => IsDefault = false;
-
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Update(TransformData copy)
{
Update(copy.Tick, copy.Position, copy.Rotation, copy.Scale, copy.ExtrapolatedPosition, copy.ParentBehaviour);
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Update(uint tick, Vector3 position, Quaternion rotation, Vector3 scale, Vector3 extrapolatedPosition, NetworkBehaviour parentBehaviour)
{
IsDefault = false;
@@ -300,6 +312,7 @@ internal void Update(uint tick, Vector3 position, Quaternion rotation, Vector3 s
ParentBehaviour = parentBehaviour;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetState()
{
IsDefault = true;
@@ -313,6 +326,7 @@ public void ResetState()
ParentBehaviour = null;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InitializeState() { }
}
#endregion
@@ -536,6 +550,23 @@ public void SetSendToOwner(bool value)
#endregion
#region Private.
+
+ #region Private Profiler Markers
+
+ private static readonly ProfilerMarker _pm_OnUpdate = new ProfilerMarker("NetworkTransform.TimeManager_OnUpdate()");
+ private static readonly ProfilerMarker _pm_OnPostTick = new ProfilerMarker("NetworkTransform.TimeManager_OnPostTick()");
+ private static readonly ProfilerMarker _pm_MoveToTarget = new ProfilerMarker("NetworkTransform.MoveToTarget(float)");
+ private static readonly ProfilerMarker _pm_UpdateTransformData = new ProfilerMarker("NetworkTransform.UpdateTransformData(ArraySegment, TransformData, TransformData, ref ChangedFull)");
+ private static readonly ProfilerMarker _pm_ForceSend0 = new ProfilerMarker("NetworkTransform.ForceSend()");
+ private static readonly ProfilerMarker _pm_ForceSend1 = new ProfilerMarker("NetworkTransform.ForceSend(uint)");
+ private static readonly ProfilerMarker _pm_SendToClients = new ProfilerMarker("NetworkTransform.SendToClients()");
+ private static readonly ProfilerMarker _pm_SendToServer = new ProfilerMarker("NetworkTransform.SendToServer(TransformData)");
+ private static readonly ProfilerMarker _pm_GetChanged = new ProfilerMarker("NetworkTransform.GetChanged(Vector3, Quaternion, Vector3, NetworkBehaviour)");
+ private static readonly ProfilerMarker _pm_SerializeChanged = new ProfilerMarker("NetworkTransform.SerializeChanged(ChangedDelta, PooledWriter, TransformData)");
+ private static readonly ProfilerMarker _pm_DataReceived = new ProfilerMarker("NetworkTransform.DataReceived(ArraySegment, Channel, bool)");
+
+ #endregion
+
///
/// Packing data with all values set to uncompressed.
///
@@ -647,17 +678,6 @@ public void SetSendToOwner(bool value)
private TimeManager _timeManager;
#endregion
- #region Private Profiler Markers
- private static readonly ProfilerMarker _pm_OnUpdate = new("NetworkTransform.TimeManager_OnUpdate()");
- private static readonly ProfilerMarker _pm_OnPostTick = new("NetworkTransform.TimeManager_OnPostTick()");
- private static readonly ProfilerMarker _pm_MoveToTarget = new("NetworkTransform.MoveToTarget(float)");
- private static readonly ProfilerMarker _pm_UpdateTransformData = new("NetworkTransform.UpdateTransformData(ArraySegment, TransformData, TransformData, ref ChangedFull)");
- private static readonly ProfilerMarker _pm_ForceSend0 = new("NetworkTransform.ForceSend()");
- private static readonly ProfilerMarker _pm_ForceSend1 = new("NetworkTransform.ForceSend(uint)");
- private static readonly ProfilerMarker _pm_SendToClients = new("NetworkTransform.SendToClients()");
- private static readonly ProfilerMarker _pm_SendToServer = new("NetworkTransform.SendToServer(TransformData)");
- #endregion
-
#region Const.
///
/// Maximum possible interpolation value.
@@ -1029,6 +1049,7 @@ public void SetExtrapolation(ushort value)
/// Returns if controlling logic can be run. This may be the server when there is no owner, even if client authoritative, and more.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanControl()
{
//Client auth.
@@ -1047,6 +1068,7 @@ private bool CanControl()
///
/// When called by the controller of this object the next changed data will be teleported to by spectators.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Teleport()
{
if (CanControl())
@@ -1066,6 +1088,7 @@ private void ObserversSetSendToOwner(bool value)
///
/// Resets last sent information to force a resend of current values after a number of ticks.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ForceSend(uint ticks)
{
using (_pm_ForceSend1.Auto())
@@ -1081,6 +1104,7 @@ public void ForceSend(uint ticks)
///
/// Resets last sent information to force a resend of current values.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ForceSend()
{
using (_pm_ForceSend0.Auto())
@@ -1112,6 +1136,7 @@ public void SetInterval(byte value)
/// Updates the interval value.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetIntervalInternal(byte value)
{
value = (byte)Mathf.Max(value, 1);
@@ -1168,17 +1193,19 @@ private void SetDefaultGoalData()
}
_teleport = false;
- SetLastReceived(_lastReceivedServerTransformData);
- SetLastReceived(_lastReceivedClientTransformData);
+ SetLastReceived(t, _lastReceivedServerTransformData, parentBehaviour);
+ SetLastReceived(t, _lastReceivedClientTransformData, parentBehaviour);
//SetInstantRates(_currentGoalData.Rates, 0, -1f);
- void SetLastReceived(TransformData td)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void SetLastReceived(Transform t, TransformData td, NetworkBehaviour parentBehaviour)
{
//Could be null if not initialized due to server or client side not being used.
if (td == null)
return;
- td.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, parentBehaviour);
+ t.GetLocalPositionAndRotation(out var localPosition, out var localRotation);
+ td.Update(0, localPosition, localRotation, t.localScale, localPosition, parentBehaviour);
}
}
@@ -1195,220 +1222,226 @@ private void LogInvalidParent()
///
private void SerializeChanged(ChangedDelta changed, PooledWriter writer, TransformData dataToUpdate = null)
{
- bool canUpdateData = dataToUpdate != null;
- if (canUpdateData && changed != ChangedDelta.Unset)
- dataToUpdate.SetIsDefaultToFalse();
-
- UpdateFlagA flagsA = UpdateFlagA.Unset;
- UpdateFlagB flagsB = UpdateFlagB.Unset;
- /* Do not use compression when childed. Depending
- * on the scale of the parent compression may
- * not be accurate enough. */
- TransformPackingData packing = ChangedContains(changed, ChangedDelta.Nested) ? _unpacked : _packing;
-
- int startIndexA = writer.Position;
- writer.Skip(1);
- //Original axis value.
- float original;
- //Compressed axis value.
- float compressed;
- //Multiplier for compression.
- float multiplier = 100f;
- /* Maximum value compressed may be
- * to send as compressed. */
- float maxValue = short.MaxValue - 1;
-
- Transform t = _cachedTransform;
- /* Position. */
- if (_synchronizePosition)
+ using (_pm_SerializeChanged.Auto())
{
- AutoPackType localPacking = packing.Position;
- //PositionX
- if (ChangedContains(changed, ChangedDelta.PositionX))
- {
- original = t.localPosition.x;
-
- if (canUpdateData)
- dataToUpdate.Position.x = original;
+ bool canUpdateData = dataToUpdate != null;
+ if (canUpdateData && changed != ChangedDelta.Unset)
+ dataToUpdate.SetIsDefaultToFalse();
- compressed = original * multiplier;
- if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
- {
- flagsA |= UpdateFlagA.X2;
- writer.WriteInt16((short)compressed);
- }
- else
- {
- flagsA |= UpdateFlagA.X4;
- writer.WriteSingle(original);
- }
- }
+ UpdateFlagA flagsA = UpdateFlagA.Unset;
+ UpdateFlagB flagsB = UpdateFlagB.Unset;
+ /* Do not use compression when childed. Depending
+ * on the scale of the parent compression may
+ * not be accurate enough. */
+ TransformPackingData packing = ChangedContains(changed, ChangedDelta.Nested) ? _unpacked : _packing;
- //PositionY
- if (ChangedContains(changed, ChangedDelta.PositionY))
- {
- original = t.localPosition.y;
-
- if (canUpdateData)
- dataToUpdate.Position.y = original;
-
- compressed = original * multiplier;
- if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
- {
- flagsA |= UpdateFlagA.Y2;
- writer.WriteInt16((short)compressed);
- }
- else
- {
- flagsA |= UpdateFlagA.Y4;
- writer.WriteSingle(original);
- }
- }
-
- //PositionZ
- if (ChangedContains(changed, ChangedDelta.PositionZ))
- {
- original = t.localPosition.z;
-
- if (canUpdateData)
- dataToUpdate.Position.z = original;
-
- compressed = original * multiplier;
- if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
- {
- flagsA |= UpdateFlagA.Z2;
- writer.WriteInt16((short)compressed);
- }
- else
- {
- flagsA |= UpdateFlagA.Z4;
- writer.WriteSingle(original);
- }
- }
- }
-
- /* Rotation. */
- if (_synchronizeRotation)
- {
- if (ChangedContains(changed, ChangedDelta.Rotation))
- {
- if (canUpdateData)
- dataToUpdate.Rotation = t.localRotation;
-
- flagsA |= UpdateFlagA.Rotation;
- /* Rotation can always use pack settings even
- * if childed. Unsual transform scale shouldn't affect rotation. */
- writer.WriteQuaternion(t.localRotation, _packing.Rotation);
- }
- }
-
- /* If there is a teleport pending then apply
- * extended flag since thats where teleport resides. */
- bool teleport = _teleport;
- if (teleport)
- changed |= ChangedDelta.Extended;
-
- if (ChangedContains(changed, ChangedDelta.Extended))
- {
- AutoPackType localPacking = packing.Scale;
- flagsA |= UpdateFlagA.Extended;
- int startIndexB = writer.Position;
+ int startIndexA = writer.Position;
writer.Skip(1);
+ //Original axis value.
+ float original;
+ //Compressed axis value.
+ float compressed;
+ //Multiplier for compression.
+ float multiplier = 100f;
+ /* Maximum value compressed may be
+ * to send as compressed. */
+ float maxValue = short.MaxValue - 1;
- /* Redundant to do the teleport check here since it was done
- * just above, but for code consistency the teleport updateflag
- * is set within this conditional with rest of the extended
- * data. */
- if (teleport)
- {
- flagsB |= UpdateFlagB.Teleport;
- _teleport = false;
- }
-
- /* Scale. */
- if (_synchronizeScale)
+ Transform t = _cachedTransform;
+ t.GetLocalPositionAndRotation(out Vector3 localPosition, out Quaternion localRotation);
+ Vector3 localScale = t.localScale;
+
+ /* Position. */
+ if (_synchronizePosition)
{
- //ScaleX
- if (ChangedContains(changed, ChangedDelta.ScaleX))
+ AutoPackType localPacking = packing.Position;
+ //PositionX
+ if (ChangedContains(changed, ChangedDelta.PositionX))
{
- original = t.localScale.x;
+ original = localPosition.x;
if (canUpdateData)
- dataToUpdate.Scale.x = original;
+ dataToUpdate.Position.x = original;
compressed = original * multiplier;
if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
{
- flagsB |= UpdateFlagB.X2;
+ flagsA |= UpdateFlagA.X2;
writer.WriteInt16((short)compressed);
}
else
{
- flagsB |= UpdateFlagB.X4;
+ flagsA |= UpdateFlagA.X4;
writer.WriteSingle(original);
}
}
- //ScaleY
- if (ChangedContains(changed, ChangedDelta.ScaleY))
+ //PositionY
+ if (ChangedContains(changed, ChangedDelta.PositionY))
{
- original = t.localScale.y;
+ original = localPosition.y;
if (canUpdateData)
- dataToUpdate.Scale.y = original;
+ dataToUpdate.Position.y = original;
compressed = original * multiplier;
if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
{
- flagsB |= UpdateFlagB.Y2;
+ flagsA |= UpdateFlagA.Y2;
writer.WriteInt16((short)compressed);
}
else
{
- flagsB |= UpdateFlagB.Y4;
+ flagsA |= UpdateFlagA.Y4;
writer.WriteSingle(original);
}
}
- //ScaleZ
- if (ChangedContains(changed, ChangedDelta.ScaleZ))
+ //PositionZ
+ if (ChangedContains(changed, ChangedDelta.PositionZ))
{
- original = t.localScale.z;
+ original = localPosition.z;
if (canUpdateData)
- dataToUpdate.Scale.z = original;
+ dataToUpdate.Position.z = original;
compressed = original * multiplier;
if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
{
- flagsB |= UpdateFlagB.Z2;
+ flagsA |= UpdateFlagA.Z2;
writer.WriteInt16((short)compressed);
}
else
{
- flagsB |= UpdateFlagB.Z4;
+ flagsA |= UpdateFlagA.Z4;
writer.WriteSingle(original);
}
}
}
- //Childed.
- if (ChangedContains(changed, ChangedDelta.Nested) && ParentBehaviour != null)
+ /* Rotation. */
+ if (_synchronizeRotation)
{
- if (canUpdateData)
- dataToUpdate.ParentBehaviour = ParentBehaviour;
+ if (ChangedContains(changed, ChangedDelta.Rotation))
+ {
+ if (canUpdateData)
+ dataToUpdate.Rotation = localRotation;
- flagsB |= UpdateFlagB.Child;
- writer.WriteNetworkBehaviour(ParentBehaviour);
+ flagsA |= UpdateFlagA.Rotation;
+ /* Rotation can always use pack settings even
+ * if childed. Unsual transform scale shouldn't affect rotation. */
+ writer.WriteQuaternion(localRotation, _packing.Rotation);
+ }
}
- writer.InsertUInt8Unpacked((byte)flagsB, startIndexB);
- }
+ /* If there is a teleport pending then apply
+ * extended flag since thats where teleport resides. */
+ bool teleport = _teleport;
+ if (teleport)
+ changed |= ChangedDelta.Extended;
+
+ if (ChangedContains(changed, ChangedDelta.Extended))
+ {
+ AutoPackType localPacking = packing.Scale;
+ flagsA |= UpdateFlagA.Extended;
+ int startIndexB = writer.Position;
+ writer.Skip(1);
+
+ /* Redundant to do the teleport check here since it was done
+ * just above, but for code consistency the teleport updateflag
+ * is set within this conditional with rest of the extended
+ * data. */
+ if (teleport)
+ {
+ flagsB |= UpdateFlagB.Teleport;
+ _teleport = false;
+ }
+
+ /* Scale. */
+ if (_synchronizeScale)
+ {
+ //ScaleX
+ if (ChangedContains(changed, ChangedDelta.ScaleX))
+ {
+ original = localScale.x;
+
+ if (canUpdateData)
+ dataToUpdate.Scale.x = original;
+
+ compressed = original * multiplier;
+ if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
+ {
+ flagsB |= UpdateFlagB.X2;
+ writer.WriteInt16((short)compressed);
+ }
+ else
+ {
+ flagsB |= UpdateFlagB.X4;
+ writer.WriteSingle(original);
+ }
+ }
+
+ //ScaleY
+ if (ChangedContains(changed, ChangedDelta.ScaleY))
+ {
+ original = localScale.y;
- //Insert flags.
- writer.InsertUInt8Unpacked((byte)flagsA, startIndexA);
+ if (canUpdateData)
+ dataToUpdate.Scale.y = original;
- bool ChangedContains(ChangedDelta whole, ChangedDelta part)
+ compressed = original * multiplier;
+ if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
+ {
+ flagsB |= UpdateFlagB.Y2;
+ writer.WriteInt16((short)compressed);
+ }
+ else
+ {
+ flagsB |= UpdateFlagB.Y4;
+ writer.WriteSingle(original);
+ }
+ }
+
+ //ScaleZ
+ if (ChangedContains(changed, ChangedDelta.ScaleZ))
+ {
+ original = localScale.z;
+
+ if (canUpdateData)
+ dataToUpdate.Scale.z = original;
+
+ compressed = original * multiplier;
+ if (localPacking != AutoPackType.Unpacked && Math.Abs(compressed) <= maxValue)
+ {
+ flagsB |= UpdateFlagB.Z2;
+ writer.WriteInt16((short)compressed);
+ }
+ else
+ {
+ flagsB |= UpdateFlagB.Z4;
+ writer.WriteSingle(original);
+ }
+ }
+ }
+
+ //Childed.
+ if (ChangedContains(changed, ChangedDelta.Nested) && ParentBehaviour != null)
+ {
+ if (canUpdateData)
+ dataToUpdate.ParentBehaviour = ParentBehaviour;
+
+ flagsB |= UpdateFlagB.Child;
+ writer.WriteNetworkBehaviour(ParentBehaviour);
+ }
+
+ writer.InsertUInt8Unpacked((byte)flagsB, startIndexB);
+ }
+
+ //Insert flags.
+ writer.InsertUInt8Unpacked((byte)flagsA, startIndexA);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool ChangedContains(ChangedDelta whole, ChangedDelta part)
{
return (whole & part) == part;
}
@@ -1504,29 +1537,32 @@ private void DeserializePacket(ArraySegment data, TransformData prevTransf
}
else
{
- Unnest();
+ Unnest(nextTransformData);
}
}
//No extended settings.
else
{
nextTransformData.Scale = prevTransformData.Scale;
- Unnest();
+ Unnest(nextTransformData);
}
- void Unnest()
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void Unnest(TransformData nextTransformData)
{
nextTransformData.ParentBehaviour = null;
}
//Returns if whole contains part.
- bool UpdateFlagAContains(UpdateFlagA whole, UpdateFlagA part)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UpdateFlagAContains(UpdateFlagA whole, UpdateFlagA part)
{
return (whole & part) == part;
}
//Returns if whole contains part.
- bool UpdateFlagBContains(UpdateFlagB whole, UpdateFlagB part)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool UpdateFlagBContains(UpdateFlagB whole, UpdateFlagB part)
{
return (whole & part) == part;
}
@@ -1647,6 +1683,8 @@ private void MoveToTarget(float delta)
//Rate to update. Changes per property.
float rate;
Transform t = _cachedTransform;
+ t.GetLocalPositionAndRotation(out Vector3 localPosition, out Quaternion localRotation);
+ Vector3 localScale = t.localScale;
//Snap any bits of the transform that should be.
SnapProperties(td);
@@ -1655,12 +1693,15 @@ private void MoveToTarget(float delta)
if (_synchronizePosition)
{
rate = rd.Position;
- Vector3 posGoal = td.ExtrapolationState == TransformData.ExtrapolateState.Active && !_lastReceiveReliable ? td.ExtrapolatedPosition : td.Position;
+ Vector3 posGoal =
+ td.ExtrapolationState == TransformData.ExtrapolateState.Active && !_lastReceiveReliable
+ ? td.ExtrapolatedPosition
+ : td.Position;
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (rate == -1f)
t.localPosition = td.Position;
else
- t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, rate * delta * multiplier);
+ t.localPosition = Vector3.MoveTowards(localPosition, posGoal, rate * delta * multiplier);
}
//Rotation.
@@ -1671,7 +1712,7 @@ private void MoveToTarget(float delta)
if (rate == -1f)
t.localRotation = td.Rotation;
else
- t.localRotation = Quaternion.RotateTowards(t.localRotation, td.Rotation, rate * delta);
+ t.localRotation = Quaternion.RotateTowards(localRotation, td.Rotation, rate * delta);
}
//Scale.
@@ -1682,7 +1723,7 @@ private void MoveToTarget(float delta)
if (rate == -1f)
t.localScale = td.Scale;
else
- t.localScale = Vector3.MoveTowards(t.localScale, td.Scale, rate * delta);
+ t.localScale = Vector3.MoveTowards(localScale, td.Scale, rate * delta);
}
float timeRemaining = rd.TimeRemaining - delta * multiplier;
@@ -1703,7 +1744,7 @@ private void MoveToTarget(float delta)
//No more in buffer, see if can extrapolate.
else
{
- /* If everything matches up then end queue.
+ /* If everything matches up then end queue.
* Otherwise let it play out until stuff
* aligns. Generally the time remaining is enough
* but every once in awhile something goes funky
@@ -1711,7 +1752,7 @@ private void MoveToTarget(float delta)
if (!HasChanged(td))
_currentGoalData = null;
OnInterpolationComplete?.Invoke();
- }
+ }
}
}
}
@@ -1763,7 +1804,8 @@ private void SendToClients()
* then a packet maybe did not arrive when expected. See if we need
* to force a reliable with the last data based on ticks passed since
* last update.*/
- if (!_authoritativeClientData.HasData && _authoritativeClientData.Channel != Channel.Reliable && _authoritativeClientData.Writer != null)
+ if (!_authoritativeClientData.HasData && _authoritativeClientData.Channel != Channel.Reliable &&
+ _authoritativeClientData.Writer != null)
{
/* If ticks have passed beyond interpolation then force
* to send reliably. */
@@ -1780,7 +1822,8 @@ private void SendToClients()
{
_changedSinceStart = true;
//Resend data from clients.
- ObserversUpdateClientAuthoritativeTransform(_authoritativeClientData.Writer.GetArraySegment(), _authoritativeClientData.Channel);
+ ObserversUpdateClientAuthoritativeTransform(_authoritativeClientData.Writer.GetArraySegment(),
+ _authoritativeClientData.Channel);
//Now being sent data can unset.
_authoritativeClientData.HasData = false;
}
@@ -1819,7 +1862,8 @@ private void SendToClients()
/* If here a send for transform values will occur. Update last values.
* Tick doesn't need to be set for whoever controls transform. */
//Transform t = _cachedTransform;
- //lastSentData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, ParentBehaviour);
+ //t.GetLocalPositionAndRotation(out var localPosition, out var localRotation);
+ //lastSentData.Update(0, localPosition, localRotation, t.localScale, localPosition, ParentBehaviour);
lastSentData.Tick = 0;
SerializeChanged(changed, writer, lastSentData);
@@ -1876,7 +1920,8 @@ private void SendToServer(TransformData lastSentTransformData)
* Tick doesn't need to be set for whoever controls transform. */
Transform t = _cachedTransform;
- //lastSentData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, ParentBehaviour);
+ //t.GetLocalPositionAndRotation(out var localPosition, out var localRotation);
+ //lastSentData.Update(0, localPosition, localRotation, t.localScale, localPosition, ParentBehaviour);
lastSentTransformData.Tick = 0;
//Send latest.
@@ -1893,10 +1938,12 @@ private void SendToServer(TransformData lastSentTransformData)
///
/// Returns if the transform differs from td.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HasChanged(TransformData td)
{
Transform t = _cachedTransform;
- bool changed = td.Position != t.localPosition || td.Rotation != t.localRotation || td.Scale != t.localScale;
+ t.GetLocalPositionAndRotation(out var localPosition, out var localRotation);
+ bool changed = td.Position != localPosition || td.Rotation != localRotation || td.Scale != t.localScale;
return changed;
}
@@ -1904,6 +1951,7 @@ private bool HasChanged(TransformData td)
///
/// Returns if there is any change between two datas.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HasChanged(TransformData a, TransformData b)
{
return a.Position != b.Position || a.Rotation != b.Rotation || a.Scale != b.Scale || a.ParentBehaviour != b.ParentBehaviour;
@@ -1941,6 +1989,7 @@ private bool HasChanged(TransformData a, TransformData b)
///
/// Gets transform values that have changed against goalData.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ChangedDelta GetChanged(TransformData transformData)
{
//If default return full changed.
@@ -1960,39 +2009,41 @@ private ChangedDelta GetChanged(TransformData transformData)
///
private ChangedDelta GetChanged(Vector3 lastPosition, Quaternion lastRotation, Vector3 lastScale, NetworkBehaviour lastParentBehaviour)
{
- ChangedDelta changed = ChangedDelta.Unset;
- Transform t = _cachedTransform;
+ using (_pm_GetChanged.Auto())
+ {
+ ChangedDelta changed = ChangedDelta.Unset;
+ Transform t = _cachedTransform;
+ t.GetLocalPositionAndRotation(out Vector3 localPosition, out Quaternion localRotation);
- Vector3 position = t.localPosition;
- if (Mathf.Abs(position.x - lastPosition.x) >= _positionSensitivity)
- changed |= ChangedDelta.PositionX;
- if (Mathf.Abs(position.y - lastPosition.y) >= _positionSensitivity)
- changed |= ChangedDelta.PositionY;
- if (Mathf.Abs(position.z - lastPosition.z) >= _positionSensitivity)
- changed |= ChangedDelta.PositionZ;
+ if (Mathf.Abs(localPosition.x - lastPosition.x) >= _positionSensitivity)
+ changed |= ChangedDelta.PositionX;
+ if (Mathf.Abs(localPosition.y - lastPosition.y) >= _positionSensitivity)
+ changed |= ChangedDelta.PositionY;
+ if (Mathf.Abs(localPosition.z - lastPosition.z) >= _positionSensitivity)
+ changed |= ChangedDelta.PositionZ;
- Quaternion rotation = t.localRotation;
- if (!rotation.Matches(lastRotation, true))
- changed |= ChangedDelta.Rotation;
+ if (!localRotation.Matches(lastRotation, true))
+ changed |= ChangedDelta.Rotation;
- ChangedDelta startChanged = changed;
+ ChangedDelta startChanged = changed;
- Vector3 scale = t.localScale;
- if (Mathf.Abs(scale.x - lastScale.x) >= _scaleSensitivity)
- changed |= ChangedDelta.ScaleX;
- if (Mathf.Abs(scale.y - lastScale.y) >= _scaleSensitivity)
- changed |= ChangedDelta.ScaleY;
- if (Mathf.Abs(scale.z - lastScale.z) >= _scaleSensitivity)
- changed |= ChangedDelta.ScaleZ;
+ Vector3 scale = t.localScale;
+ if (Mathf.Abs(scale.x - lastScale.x) >= _scaleSensitivity)
+ changed |= ChangedDelta.ScaleX;
+ if (Mathf.Abs(scale.y - lastScale.y) >= _scaleSensitivity)
+ changed |= ChangedDelta.ScaleY;
+ if (Mathf.Abs(scale.z - lastScale.z) >= _scaleSensitivity)
+ changed |= ChangedDelta.ScaleZ;
- if (changed != ChangedDelta.Unset && ParentBehaviour != null)
- changed |= ChangedDelta.Nested;
+ if (changed != ChangedDelta.Unset && ParentBehaviour != null)
+ changed |= ChangedDelta.Nested;
- //If added scale or childed then also add extended.
- if (startChanged != changed)
- changed |= ChangedDelta.Extended;
+ //If added scale or childed then also add extended.
+ if (startChanged != changed)
+ changed |= ChangedDelta.Extended;
- return changed;
+ return changed;
+ }
}
#endregion
@@ -2008,36 +2059,38 @@ private void SnapProperties(TransformData transformData, bool force = false)
transformData.SnappingChecked = true;
Transform t = _cachedTransform;
-
+ t.GetLocalPositionAndRotation(out Vector3 startPosition, out Quaternion startRotation);
+
//Position.
if (_synchronizePosition)
{
- Vector3 startPosition = t.localPosition;
Vector3 position;
- position.x = force || _positionSnapping.X ? transformData.Position.x : t.localPosition.x;
- position.y = force || _positionSnapping.Y ? transformData.Position.y : t.localPosition.y;
- position.z = force || _positionSnapping.Z ? transformData.Position.z : t.localPosition.z;
+ position.x = force || _positionSnapping.X ? transformData.Position.x : startPosition.x;
+ position.y = force || _positionSnapping.Y ? transformData.Position.y : startPosition.y;
+ position.z = force || _positionSnapping.Z ? transformData.Position.z : startPosition.z;
t.localPosition = position;
}
//Rotation.
if (_synchronizeRotation)
{
- Vector3 eulers;
+ Vector3 startEulers = startRotation.eulerAngles;
Vector3 goalEulers = transformData.Rotation.eulerAngles;
- eulers.x = force || _rotationSnapping.X ? goalEulers.x : t.localEulerAngles.x;
- eulers.y = force || _rotationSnapping.Y ? goalEulers.y : t.localEulerAngles.y;
- eulers.z = force || _rotationSnapping.Z ? goalEulers.z : t.localEulerAngles.z;
+ Vector3 eulers;
+ eulers.x = force || _rotationSnapping.X ? goalEulers.x : startEulers.x;
+ eulers.y = force || _rotationSnapping.Y ? goalEulers.y : startEulers.y;
+ eulers.z = force || _rotationSnapping.Z ? goalEulers.z : startEulers.z;
t.localEulerAngles = eulers;
}
//Scale.
if (_synchronizeScale)
{
+ var startScale = t.localScale;
Vector3 scale;
- scale.x = force || _scaleSnapping.X ? transformData.Scale.x : t.localScale.x;
- scale.y = force || _scaleSnapping.Y ? transformData.Scale.y : t.localScale.y;
- scale.z = force || _scaleSnapping.Z ? transformData.Scale.z : t.localScale.z;
+ scale.x = force || _scaleSnapping.X ? transformData.Scale.x : startScale.x;
+ scale.y = force || _scaleSnapping.Y ? transformData.Scale.y : startScale.y;
+ scale.z = force || _scaleSnapping.Z ? transformData.Scale.z : startScale.z;
t.localScale = scale;
}
}
@@ -2045,6 +2098,7 @@ private void SnapProperties(TransformData transformData, bool force = false)
///
/// Sets move rates which will occur instantly.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetInstantRates(RateData rd, uint tickDifference, float timeRemaining)
{
//Was default to 1 tickDiff and -1 time remaining.
@@ -2207,7 +2261,8 @@ private void SetCalculatedRates(TransformData prevTd, RateData prevRd, GoalData
rd.Update(positionRate, rotationRate, scaleRate, unalteredPositionRate, tickDifference, timePassed);
//Returns if whole contains part.
- bool ChangedFullContains(ChangedFull whole, ChangedFull part)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool ChangedFullContains(ChangedFull whole, ChangedFull part)
{
return (whole & part) == part;
}
@@ -2216,7 +2271,8 @@ bool ChangedFullContains(ChangedFull whole, ChangedFull part)
* This is used to decide if a property should be teleported.
* When distances are exceptionally small smoothing rate
* calculations may result as an invalid value. */
- bool LowDistance(float dist, bool rotation)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool LowDistance(float dist, bool rotation)
{
if (rotation)
return dist < 1f;
@@ -2228,6 +2284,7 @@ bool LowDistance(float dist, bool rotation)
///
/// Gets the tick difference between two GoalDatas.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint GetTickDifference(TransformData prevTd, GoalData nextGd, uint minimum, out float timePassed)
{
TransformData nextTd = nextGd.Transforms;
@@ -2251,12 +2308,12 @@ private uint GetTickDifference(TransformData prevTd, GoalData nextGd, uint minim
///
/// Sets extrapolation data on next.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetExtrapolatedData(TransformData prev, TransformData next, Channel channel)
{
//Default value.
next.ExtrapolationState = TransformData.ExtrapolateState.Disabled;
-
- }
+ }
///
/// Updates a client with transform data.
@@ -2323,108 +2380,112 @@ private void ServerUpdateTransform(ArraySegment data, Channel channel)
}
///
- /// Processes received data for lcients and server.
+ /// Processes received data for clients and server.
///
private void DataReceived(ArraySegment data, Channel channel, bool asServer)
{
- if (IsDeinitializing)
- return;
-
- TransformData prevTd = asServer ? _lastReceivedClientTransformData : _lastReceivedServerTransformData;
- RateData prevRd = _lastCalculatedRateData;
+ using (_pm_DataReceived.Auto())
+ {
+ if (IsDeinitializing)
+ return;
- ChangedFull changedFull = ChangedFull.Unset;
- GoalData nextGd = ResettableObjectCaches.Retrieve();
- TransformData nextTd = nextGd.Transforms;
- UpdateTransformData(data, prevTd, nextTd, ref changedFull);
+ TransformData prevTd = asServer ? _lastReceivedClientTransformData : _lastReceivedServerTransformData;
+ RateData prevRd = _lastCalculatedRateData;
- OnDataReceived?.Invoke(prevTd, nextTd);
- SetExtrapolatedData(prevTd, nextTd, channel);
+ ChangedFull changedFull = ChangedFull.Unset;
+ GoalData nextGd = ResettableObjectCaches.Retrieve();
+ TransformData nextTd = nextGd.Transforms;
+ UpdateTransformData(data, prevTd, nextTd, ref changedFull);
- bool hasChanged = HasChanged(prevTd, nextTd);
+ OnDataReceived?.Invoke(prevTd, nextTd);
+ SetExtrapolatedData(prevTd, nextTd, channel);
- //If server only teleport.
- if (asServer && !IsClientStarted)
- {
- uint tickDifference = GetTickDifference(prevTd, nextGd, 1, out float timePassed);
- SetInstantRates(nextGd.Rates, tickDifference, timePassed);
- }
- //Otherwise use timed.
- else
- {
- SetCalculatedRates(prevTd, prevRd, nextGd, changedFull, hasChanged, channel);
- }
+ bool hasChanged = HasChanged(prevTd, nextTd);
- _lastReceiveReliable = channel == Channel.Reliable;
- /* If channel is reliable then this is a settled packet.
- * Set tick to UNSET. When this occurs time calculations
- * assume only 1 tick has passed. */
- if (channel == Channel.Reliable)
- nextTd.Tick = TimeManager.UNSET_TICK;
-
- prevTd.Update(nextTd);
- prevRd.Update(nextGd.Rates);
-
- nextGd.ReceivedTick = _timeManager.LocalTick;
-
- bool currentDataNull = _currentGoalData == null;
- /* If extrapolating then immediately break the extrapolation
- * in favor of newest results. This will keep the buffer
- * at 0 until the transform settles but the only other option is
- * to stop the movement, which would defeat purpose of extrapolation,
- * or slow down the transform while buffer rebuilds. Neither choice
- * is great but later on I might try slowing down the transform slightly
- * to give the buffer a chance to rebuild. */
- if (!currentDataNull && _currentGoalData.Transforms.ExtrapolationState == TransformData.ExtrapolateState.Active)
- {
- SetCurrentGoalData(nextGd);
- }
- /* If queue isn't started and its buffered enough
- * to satisfy interpolation then set ready
- * and set current data.
- *
- * Also if reliable then begin moving. */
- else if ((currentDataNull && _goalDataQueue.Count >= _interpolation) || channel == Channel.Reliable)
- {
- if (_goalDataQueue.Count > 0)
+ //If server only teleport.
+ if (asServer && !IsClientStarted)
{
- SetCurrentGoalData(_goalDataQueue.Dequeue());
- /* If is reliable and has changed then also
- * enqueue latest. */
- if (hasChanged)
- _goalDataQueue.Enqueue(nextGd);
+ uint tickDifference = GetTickDifference(prevTd, nextGd, 1, out float timePassed);
+ SetInstantRates(nextGd.Rates, tickDifference, timePassed);
}
+ //Otherwise use timed.
else
{
- SetCurrentGoalData(nextGd);
+ SetCalculatedRates(prevTd, prevRd, nextGd, changedFull, hasChanged, channel);
}
- }
- /* If here then there's not enough in buffer to begin
- * so add onto the buffer. */
- else
- {
- _goalDataQueue.Enqueue(nextGd);
- }
- /* If the queue is excessive beyond interpolation then
- * dequeue extras to prevent from dropping behind too
- * quickly. This shouldn't be an issue with normal movement
- * as the NT speeds up if the buffer unexpectedly grows, but
- * when connections are unstable results may come in chunks
- * and for a better experience the older parts of the chunks
- * will be dropped. */
- if (_goalDataQueue.Count > _interpolation + 3)
- {
- while (_goalDataQueue.Count > _interpolation)
+ _lastReceiveReliable = channel == Channel.Reliable;
+ /* If channel is reliable then this is a settled packet.
+ * Set tick to UNSET. When this occurs time calculations
+ * assume only 1 tick has passed. */
+ if (channel == Channel.Reliable)
+ nextTd.Tick = TimeManager.UNSET_TICK;
+
+ prevTd.Update(nextTd);
+ prevRd.Update(nextGd.Rates);
+
+ nextGd.ReceivedTick = _timeManager.LocalTick;
+
+ bool currentDataNull = _currentGoalData == null;
+ /* If extrapolating then immediately break the extrapolation
+ * in favor of newest results. This will keep the buffer
+ * at 0 until the transform settles but the only other option is
+ * to stop the movement, which would defeat purpose of extrapolation,
+ * or slow down the transform while buffer rebuilds. Neither choice
+ * is great but later on I might try slowing down the transform slightly
+ * to give the buffer a chance to rebuild. */
+ if (!currentDataNull && _currentGoalData.Transforms.ExtrapolationState ==
+ TransformData.ExtrapolateState.Active)
{
- GoalData tmpGd = _goalDataQueue.Dequeue();
- ResettableObjectCaches.Store(tmpGd);
+ SetCurrentGoalData(nextGd);
+ }
+ /* If queue isn't started and its buffered enough
+ * to satisfy interpolation then set ready
+ * and set current data.
+ *
+ * Also if reliable then begin moving. */
+ else if ((currentDataNull && _goalDataQueue.Count >= _interpolation) || channel == Channel.Reliable)
+ {
+ if (_goalDataQueue.Count > 0)
+ {
+ SetCurrentGoalData(_goalDataQueue.Dequeue());
+ /* If is reliable and has changed then also
+ * enqueue latest. */
+ if (hasChanged)
+ _goalDataQueue.Enqueue(nextGd);
+ }
+ else
+ {
+ SetCurrentGoalData(nextGd);
+ }
+ }
+ /* If here then there's not enough in buffer to begin
+ * so add onto the buffer. */
+ else
+ {
+ _goalDataQueue.Enqueue(nextGd);
}
- //Snap to the next data to fix any smoothing timings.
- SetCurrentGoalData(_goalDataQueue.Dequeue());
- SetInstantRates(_currentGoalData!.Rates, 1, -1f);
- SnapProperties(_currentGoalData.Transforms, true);
+ /* If the queue is excessive beyond interpolation then
+ * dequeue extras to prevent from dropping behind too
+ * quickly. This shouldn't be an issue with normal movement
+ * as the NT speeds up if the buffer unexpectedly grows, but
+ * when connections are unstable results may come in chunks
+ * and for a better experience the older parts of the chunks
+ * will be dropped. */
+ if (_goalDataQueue.Count > _interpolation + 3)
+ {
+ while (_goalDataQueue.Count > _interpolation)
+ {
+ GoalData tmpGd = _goalDataQueue.Dequeue();
+ ResettableObjectCaches.Store(tmpGd);
+ }
+
+ //Snap to the next data to fix any smoothing timings.
+ SetCurrentGoalData(_goalDataQueue.Dequeue());
+ SetInstantRates(_currentGoalData!.Rates, 1, -1f);
+ SnapProperties(_currentGoalData.Transforms, true);
+ }
}
}
diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
index d3e6ca6d..7dbb8bc2 100644
--- a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
@@ -143,11 +143,39 @@ public void SetFrameRate(ushort value)
private SplitReader _splitReader = new();
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#endregion
#region Private Profiler Markers
private static readonly ProfilerMarker _pm_OnPostTick = new("ClientManager.TimeManager_OnPostTick()");
+ private static readonly ProfilerMarker _pm_Transport_OnClientConnectionState =
+ new("ClientManager.Transport_OnClientConnectionState(ClientConnectionStateArgs)");
+ private static readonly ProfilerMarker _pm_Transport_OnClientReceivedData =
+ new("ClientManager.Transport_OnClientReceivedData(ClientReceivedDataArgs)");
+ private static readonly ProfilerMarker _pm_TransportManager_OnIterateIncomingEnd =
+ new("ClientManager.TransportManager_OnIterateIncomingEnd(bool)");
+ private static readonly ProfilerMarker _pm_ParseReceived =
+ new("ClientManager.ParseReceived(ClientReceivedDataArgs)");
+ private static readonly ProfilerMarker _pm_ParseReader =
+ new("ClientManager.ParseReader(PooledReader, Channel, bool)");
+ private static readonly ProfilerMarker _pm_ParseReader_ReadPacketId =
+ new("ClientManager.ParseReader.ReadPacketId()");
+ private static readonly ProfilerMarker _pm_ParseReader_HandlePacket =
+ new("ClientManager.ParseReader.HandlePacket()");
+ private static readonly ProfilerMarker _pm_ParseReader_StateUpdate =
+ new("ClientManager.ParseReader.StateUpdate()");
+ private static readonly ProfilerMarker _pm_ParseReader_Broadcast =
+ new("ClientManager.ParseReader.Broadcast()");
+ private static readonly ProfilerMarker _pm_ParseReader_PingPong =
+ new("ClientManager.ParseReader.PingPong()");
+ private static readonly ProfilerMarker _pm_ParseReader_TimingUpdate =
+ new("ClientManager.ParseReader.TimingUpdate()");
+ private static readonly ProfilerMarker _pm_ParseReader_Authenticated =
+ new("ClientManager.ParseReader.Authenticated()");
+ private static readonly ProfilerMarker _pm_ParseReader_Disconnect =
+ new("ClientManager.ParseReader.Disconnect()");
+ private static readonly ProfilerMarker _pm_ParseReader_Version =
+ new("ClientManager.ParseReader.Version()");
#endregion
private void OnDestroy()
@@ -309,39 +337,42 @@ public bool StartConnection(string address, ushort port)
///
private void Transport_OnClientConnectionState(ClientConnectionStateArgs args)
{
- LocalConnectionState state = args.ConnectionState;
- Started = state == LocalConnectionState.Started;
- Objects.OnClientConnectionState(args);
-
- // Clear connection after so objects can update using current Connection value.
- if (!Started)
- {
- Connection = NetworkManager.EmptyConnection;
- NetworkManager.ClearClientsCollection(Clients);
- }
- else
+ using (_pm_Transport_OnClientConnectionState.Auto())
{
- _lastPacketTime = Time.unscaledTime;
- // Send version.
- PooledWriter writer = WriterPool.Retrieve();
- writer.WritePacketIdUnpacked(PacketId.Version);
- writer.WriteString(NetworkManager.FISHNET_VERSION);
- NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment());
- WriterPool.Store(writer);
- }
+ LocalConnectionState state = args.ConnectionState;
+ Started = state == LocalConnectionState.Started;
+ Objects.OnClientConnectionState(args);
- if (NetworkManager.CanLog(LoggingType.Common))
- {
- Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex);
- string tName = t == null ? "Unknown" : t.GetType().Name;
- string socketInformation = string.Empty;
- if (state == LocalConnectionState.Starting)
- socketInformation = $" Server IP is {t.GetClientAddress()}, port is {t.GetPort()}.";
- NetworkManager.Log($"Local client is {state.ToString().ToLower()} for {tName}.{socketInformation}");
- }
+ // Clear connection after so objects can update using current Connection value.
+ if (!Started)
+ {
+ Connection = NetworkManager.EmptyConnection;
+ NetworkManager.ClearClientsCollection(Clients);
+ }
+ else
+ {
+ _lastPacketTime = Time.unscaledTime;
+ // Send version.
+ PooledWriter writer = WriterPool.Retrieve();
+ writer.WritePacketIdUnpacked(PacketId.Version);
+ writer.WriteString(NetworkManager.FISHNET_VERSION);
+ NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment());
+ WriterPool.Store(writer);
+ }
+
+ if (NetworkManager.CanLog(LoggingType.Common))
+ {
+ Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex);
+ string tName = t == null ? "Unknown" : t.GetType().Name;
+ string socketInformation = string.Empty;
+ if (state == LocalConnectionState.Starting)
+ socketInformation = $" Server IP is {t.GetClientAddress()}, port is {t.GetPort()}.";
+ NetworkManager.Log($"Local client is {state.ToString().ToLower()} for {tName}.{socketInformation}");
+ }
- NetworkManager.UpdateFramerate();
- OnClientConnectionState?.Invoke(args);
+ NetworkManager.UpdateFramerate();
+ OnClientConnectionState?.Invoke(args);
+ }
}
///
@@ -349,7 +380,10 @@ private void Transport_OnClientConnectionState(ClientConnectionStateArgs args)
///
private void Transport_OnClientReceivedData(ClientReceivedDataArgs args)
{
- ParseReceived(args);
+ using (_pm_Transport_OnClientReceivedData.Auto())
+ {
+ ParseReceived(args);
+ }
}
///
@@ -357,15 +391,18 @@ private void Transport_OnClientReceivedData(ClientReceivedDataArgs args)
///
private void TransportManager_OnIterateIncomingEnd(bool server)
{
- /* Should the last packet received be a spawn or despawn
- * then the cache won't yet be iterated because it only
- * iterates when a packet is anything but those two. Because
- * of such if any object caches did come in they must be iterated
- * at the end of the incoming cycle. This isn't as clean as I'd
- * like but it does ensure there will be no missing network object
- * references on spawned objects. */
- if (Started && !server)
- Objects.IterateObjectCache();
+ using (_pm_TransportManager_OnIterateIncomingEnd.Auto())
+ {
+ /* Should the last packet received be a spawn or despawn
+ * then the cache won't yet be iterated because it only
+ * iterates when a packet is anything but those two. Because
+ * of such if any object caches did come in they must be iterated
+ * at the end of the incoming cycle. This isn't as clean as I'd
+ * like but it does ensure there will be no missing network object
+ * references on spawned objects. */
+ if (Started && !server)
+ Objects.IterateObjectCache();
+ }
}
///
@@ -373,198 +410,231 @@ private void TransportManager_OnIterateIncomingEnd(bool server)
///
private void ParseReceived(ClientReceivedDataArgs args)
{
- #if DEVELOPMENT && !UNITY_SERVER
- if (_networkTrafficStatistics != null)
- _networkTrafficStatistics.PacketBundleReceived(asServer: false);
- #endif
+ using (_pm_ParseReceived.Auto())
+ {
+ #if DEVELOPMENT && !UNITY_SERVER
+ if (_networkTrafficStatistics != null)
+ _networkTrafficStatistics.PacketBundleReceived(asServer: false);
+ #endif
- _lastPacketTime = Time.unscaledTime;
+ _lastPacketTime = Time.unscaledTime;
- ArraySegment segment;
- if (NetworkManager.TransportManager.HasIntermediateLayer)
- segment = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, true);
- else
- segment = args.Data;
+ ArraySegment segment;
+ if (NetworkManager.TransportManager.HasIntermediateLayer)
+ segment = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, true);
+ else
+ segment = args.Data;
- if (_networkTrafficStatistics != null)
- _networkTrafficStatistics.AddInboundSocketData((ulong)segment.Count, asServer: false);
+ if (_networkTrafficStatistics != null)
+ _networkTrafficStatistics.AddInboundSocketData((ulong)segment.Count, asServer: false);
- if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH)
- return;
+ if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH)
+ return;
- PooledReader reader = ReaderPool.Retrieve(segment, NetworkManager, Reader.DataSource.Server);
- TimeManager tm = NetworkManager.TimeManager;
- tm.LastPacketTick.Update(reader.ReadTickUnpacked(), EstimatedTick.OldTickOption.Discard, false);
- ParseReader(reader, args.Channel);
- ReaderPool.Store(reader);
+ PooledReader reader = ReaderPool.Retrieve(segment, NetworkManager, Reader.DataSource.Server);
+ TimeManager tm = NetworkManager.TimeManager;
+ tm.LastPacketTick.Update(reader.ReadTickUnpacked(), EstimatedTick.OldTickOption.Discard, false);
+ ParseReader(reader, args.Channel);
+ ReaderPool.Store(reader);
+ }
}
internal void ParseReader(PooledReader reader, Channel channel, bool print = false)
{
- PacketId packetId = PacketId.Unset;
- #if !DEVELOPMENT
- try
- {
- #endif
- Reader.DataSource dataSource = Reader.DataSource.Server;
- /* This is a special condition where a message may arrive split.
- * When this occurs buffer each packet until all packets are
- * received. */
- if (reader.PeekPacketId() == PacketId.Split)
+ using (_pm_ParseReader.Auto())
{
- #if DEVELOPMENT
- NetworkManager.PacketIdHistory.ReceivedPacket(PacketId.Split, packetFromServer: true);
- #endif
- // Skip packetId.
- reader.ReadPacketId();
- int expectedMessages;
- _splitReader.GetHeader(reader, out expectedMessages);
- _splitReader.Write(NetworkManager.TimeManager.LastPacketTick.LastRemoteTick, reader, expectedMessages);
- /* If fullMessage returns 0 count then the split
- * has not written fully yet. Otherwise, if there is
- * data within then reinitialize reader with the
- * full message. */
- ArraySegment fullMessage = _splitReader.GetFullMessage();
- if (fullMessage.Count == 0)
- return;
-
- reader.Initialize(fullMessage, NetworkManager, dataSource);
- }
-
- while (reader.Remaining > 0)
- {
- packetId = reader.ReadPacketId();
- #if DEVELOPMENT
- NetworkManager.PacketIdHistory.ReceivedPacket(packetId, packetFromServer: true);
- // if (!NetworkManager.IsServerStarted)
- // print = true;
- // if (print)
- // {
- // if (packetId == PacketId.ObserversRpc)
- // Debug.Log($"PacketId {packetId} - Remaining {reader.Remaining}.");
- // else
- // Debug.LogWarning($"PacketId {packetId} - Remaining {reader.Remaining}.");
- // }
- // print = false;
+ PacketId packetId = PacketId.Unset;
+ #if !DEVELOPMENT
+ try
+ {
#endif
- bool spawnOrDespawn = packetId == PacketId.ObjectSpawn || packetId == PacketId.ObjectDespawn;
- /* Length of data. Only available if using unreliable. Unreliable packets
- * can arrive out of order which means object orientated messages such as RPCs may
- * arrive after the object for which they target has already been destroyed. When this happens
- * on lesser solutions they just dump the entire packet. However, since FishNet batches data.
- * it's very likely a packet will contain more than one packetId. With this mind, length is
- * sent as well so if any reason the data does have to be dumped it will only be dumped for
- * that single packetId but not the rest. Broadcasts don't need length either even if unreliable
- * because they are not object bound. */
-
- // Is spawn or despawn; cache packet.
- if (spawnOrDespawn)
+ Reader.DataSource dataSource = Reader.DataSource.Server;
+ /* This is a special condition where a message may arrive split.
+ * When this occurs buffer each packet until all packets are
+ * received. */
+ if (reader.PeekPacketId() == PacketId.Split)
{
- if (packetId == PacketId.ObjectSpawn)
- Objects.ReadSpawn(reader);
- else if (packetId == PacketId.ObjectDespawn)
- Objects.CacheDespawn(reader);
+ #if DEVELOPMENT
+ NetworkManager.PacketIdHistory.ReceivedPacket(PacketId.Split, packetFromServer: true);
+ #endif
+ // Skip packetId.
+ reader.ReadPacketId();
+ int expectedMessages;
+ _splitReader.GetHeader(reader, out expectedMessages);
+ _splitReader.Write(NetworkManager.TimeManager.LastPacketTick.LastRemoteTick, reader, expectedMessages);
+ /* If fullMessage returns 0 count then the split
+ * has not written fully yet. Otherwise, if there is
+ * data within then reinitialize reader with the
+ * full message. */
+ ArraySegment fullMessage = _splitReader.GetFullMessage();
+ if (fullMessage.Count == 0)
+ return;
+
+ reader.Initialize(fullMessage, NetworkManager, dataSource);
}
- // Not spawn or despawn.
- else
+
+ while (reader.Remaining > 0)
{
- /* Iterate object cache should any of the
- * incoming packets rely on it. Objects
- * in cache will always be received before any messages
- * that use them. */
- Objects.IterateObjectCache();
- // Then process packet normally.
- if ((ushort)packetId >= NetworkManager.StartingRpcLinkIndex)
- {
- Objects.ParseRpcLink(reader, (ushort)packetId, channel);
- }
- else if (packetId == PacketId.StateUpdate)
- {
- NetworkManager.PredictionManager.ParseStateUpdate(reader, channel);
- }
- else if (packetId == PacketId.Replicate)
- {
- Objects.ParseReplicateRpc(reader, null, channel);
- }
- else if (packetId == PacketId.Reconcile)
- {
- Objects.ParseReconcileRpc(reader, channel);
- }
- else if (packetId == PacketId.ObserversRpc)
+ using (_pm_ParseReader_ReadPacketId.Auto())
{
- Objects.ParseObserversRpc(reader, channel);
- }
- else if (packetId == PacketId.TargetRpc)
- {
- Objects.ParseTargetRpc(reader, channel);
- }
- else if (packetId == PacketId.Broadcast)
- {
- ParseBroadcast(reader, channel);
- }
- else if (packetId == PacketId.PingPong)
- {
- ParsePingPong(reader);
- }
- else if (packetId == PacketId.SyncType)
- {
- Objects.ParseSyncType(reader, channel);
- }
- else if (packetId == PacketId.PredictedSpawnResult)
- {
- Objects.ParsePredictedSpawnResult(reader);
- }
- else if (packetId == PacketId.TimingUpdate)
- {
- NetworkManager.TimeManager.ParseTimingUpdate(reader);
- }
- else if (packetId == PacketId.OwnershipChange)
- {
- Objects.ParseOwnershipChange(reader);
- }
- else if (packetId == PacketId.Authenticated)
- {
- ParseAuthenticated(reader);
- }
- else if (packetId == PacketId.Disconnect)
- {
- reader.Clear();
- StopConnection();
+ packetId = reader.ReadPacketId();
+ #if DEVELOPMENT
+ NetworkManager.PacketIdHistory.ReceivedPacket(packetId, packetFromServer: true);
+ // if (!NetworkManager.IsServerStarted)
+ // print = true;
+ // if (print)
+ // {
+ // if (packetId == PacketId.ObserversRpc)
+ // Debug.Log($"PacketId {packetId} - Remaining {reader.Remaining}.");
+ // else
+ // Debug.LogWarning($"PacketId {packetId} - Remaining {reader.Remaining}.");
+ // }
+ // print = false;
+ #endif
}
- else if (packetId == PacketId.Version)
+ bool spawnOrDespawn = packetId == PacketId.ObjectSpawn || packetId == PacketId.ObjectDespawn;
+ /* Length of data. Only available if using unreliable. Unreliable packets
+ * can arrive out of order which means object orientated messages such as RPCs may
+ * arrive after the object for which they target has already been destroyed. When this happens
+ * on lesser solutions they just dump the entire packet. However, since FishNet batches data.
+ * it's very likely a packet will contain more than one packetId. With this mind, length is
+ * sent as well so if any reason the data does have to be dumped it will only be dumped for
+ * that single packetId but not the rest. Broadcasts don't need length either even if unreliable
+ * because they are not object bound. */
+
+ // Is spawn or despawn; cache packet.
+ if (spawnOrDespawn)
{
- ParseVersion(reader);
+ if (packetId == PacketId.ObjectSpawn)
+ Objects.ReadSpawn(reader);
+ else if (packetId == PacketId.ObjectDespawn)
+ Objects.CacheDespawn(reader);
}
+ // Not spawn or despawn.
else
{
- NetworkManager.LogError($"Client received an unhandled PacketId of {(ushort)packetId} on channel {channel}. Remaining data has been purged.");
- #if DEVELOPMENT
- NetworkManager.LogError(NetworkManager.PacketIdHistory.GetReceivedPacketIds(packetsFromServer: true));
- #endif
- return;
+ /* Iterate object cache should any of the
+ * incoming packets rely on it. Objects
+ * in cache will always be received before any messages
+ * that use them. */
+ Objects.IterateObjectCache();
+ using (_pm_ParseReader_HandlePacket.Auto())
+ {
+ // Then process packet normally.
+ if ((ushort)packetId >= NetworkManager.StartingRpcLinkIndex)
+ {
+ Objects.ParseRpcLink(reader, (ushort)packetId, channel);
+ }
+ else if (packetId == PacketId.StateUpdate)
+ {
+ using (_pm_ParseReader_StateUpdate.Auto())
+ {
+ NetworkManager.PredictionManager.ParseStateUpdate(reader, channel);
+ }
+ }
+ else if (packetId == PacketId.Replicate)
+ {
+ Objects.ParseReplicateRpc(reader, null, channel);
+ }
+ else if (packetId == PacketId.Reconcile)
+ {
+ Objects.ParseReconcileRpc(reader, channel);
+ }
+ else if (packetId == PacketId.ObserversRpc)
+ {
+ Objects.ParseObserversRpc(reader, channel);
+ }
+ else if (packetId == PacketId.TargetRpc)
+ {
+ Objects.ParseTargetRpc(reader, channel);
+ }
+ else if (packetId == PacketId.Broadcast)
+ {
+ using (_pm_ParseReader_Broadcast.Auto())
+ {
+ ParseBroadcast(reader, channel);
+ }
+ }
+ else if (packetId == PacketId.PingPong)
+ {
+ using (_pm_ParseReader_PingPong.Auto())
+ {
+ ParsePingPong(reader);
+ }
+ }
+ else if (packetId == PacketId.SyncType)
+ {
+ Objects.ParseSyncType(reader, channel);
+ }
+ else if (packetId == PacketId.PredictedSpawnResult)
+ {
+ Objects.ParsePredictedSpawnResult(reader);
+ }
+ else if (packetId == PacketId.TimingUpdate)
+ {
+ using (_pm_ParseReader_TimingUpdate.Auto())
+ {
+ NetworkManager.TimeManager.ParseTimingUpdate(reader);
+ }
+ }
+ else if (packetId == PacketId.OwnershipChange)
+ {
+ Objects.ParseOwnershipChange(reader);
+ }
+ else if (packetId == PacketId.Authenticated)
+ {
+ using (_pm_ParseReader_Authenticated.Auto())
+ {
+ ParseAuthenticated(reader);
+ }
+ }
+ else if (packetId == PacketId.Disconnect)
+ {
+ using (_pm_ParseReader_Disconnect.Auto())
+ {
+ reader.Clear();
+ StopConnection();
+ }
+ }
+ else if (packetId == PacketId.Version)
+ {
+ using (_pm_ParseReader_Version.Auto())
+ {
+ ParseVersion(reader);
+ }
+ }
+ else
+ {
+ NetworkManager.LogError($"Client received an unhandled PacketId of {(ushort)packetId} on channel {channel}. Remaining data has been purged.");
+ #if DEVELOPMENT
+ NetworkManager.LogError(NetworkManager.PacketIdHistory.GetReceivedPacketIds(packetsFromServer: true));
+ #endif
+ return;
+ }
+ }
}
+
+ #if DEVELOPMENT
+ if (print)
+ Debug.Log($"Reader remaining {reader.Remaining}");
+ #endif
}
- #if DEVELOPMENT
- if (print)
- Debug.Log($"Reader remaining {reader.Remaining}");
+ /* Iterate cache when reader is emptied.
+ * This is incase the last packet received
+ * was a spawned, which wouldn't trigger
+ * the above iteration. There's no harm
+ * in doing this check multiple times as there's
+ * an exit early check. */
+ Objects.IterateObjectCache();
+ #if !DEVELOPMENT
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError($"Client encountered an error while parsing data for packetId {packetId}. Message: {e.Message}.");
+ }
#endif
}
-
- /* Iterate cache when reader is emptied.
- * This is incase the last packet received
- * was a spawned, which wouldn't trigger
- * the above iteration. There's no harm
- * in doing this check multiple times as there's
- * an exit early check. */
- Objects.IterateObjectCache();
- #if !DEVELOPMENT
- }
- catch (Exception e)
- {
- NetworkManager.LogError($"Client encountered an error while parsing data for packetId {packetId}. Message: {e.Message}.");
- }
- #endif
}
///
@@ -716,4 +786,4 @@ private void CheckServerTimeout()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs
index f4ad7e60..46c95ad2 100644
--- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs
@@ -8,6 +8,7 @@
using FishNet.Transporting;
using GameKit.Dependencies.Utilities;
using System.Collections.Generic;
+using Unity.Profiling;
namespace FishNet.Managing.Client
{
@@ -23,6 +24,17 @@ public partial class ClientObjects : ManagedObjects
private Dictionary _rpcLinks = new();
#endregion
+ #region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_ParseRpcLink =
+ new("ClientObjects.ParseRpcLink(PooledReader, ushort, Channel)");
+ private static readonly ProfilerMarker _pm_ParseRpcLink_TargetRpc =
+ new("NetworkBehaviour.ReadTargetRpc()");
+ private static readonly ProfilerMarker _pm_ParseRpcLink_ObserversRpc =
+ new("NetworkBehaviour.ReadObserversRpc()");
+ private static readonly ProfilerMarker _pm_ParseRpcLink_Reconcile =
+ new("NetworkBehaviour.OnReconcileRpc()");
+ #endregion
+
///
/// Parses a received RPCLink.
///
@@ -30,49 +42,63 @@ public partial class ClientObjects : ManagedObjects
///
internal void ParseRpcLink(PooledReader reader, ushort index, Channel channel)
{
-#if DEVELOPMENT
- NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
-#endif
- int readerStartAfterDebug = reader.Position;
-
- int dataLength;
- // Link index isn't stored.
- if (!_rpcLinks.TryGetValueIL2CPP(index, out RpcLink link))
- {
- dataLength = Packets.GetPacketLength(ushort.MaxValue, reader, channel);
- SkipDataLength(index, reader, dataLength);
- }
- // Found NetworkObject for link.
- else if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob))
+ using (_pm_ParseRpcLink.Auto())
{
- // Still call GetPacketLength to remove any extra bytes at the front of the reader.
- NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex];
- if (link.RpcPacketId == PacketId.TargetRpc)
+ #if DEVELOPMENT
+ NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining,
+ out string rpcInformation, out uint expectedReadAmount);
+ #endif
+ int readerStartAfterDebug = reader.Position;
+
+ int dataLength;
+ // Link index isn't stored.
+ if (!_rpcLinks.TryGetValueIL2CPP(index, out RpcLink link))
{
- Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel);
- nb.ReadTargetRpc(readerStartAfterDebug, fromRpcLink: true, link.RpcHash, reader, channel);
+ dataLength = Packets.GetPacketLength(ushort.MaxValue, reader, channel);
+ SkipDataLength(index, reader, dataLength);
}
- else if (link.RpcPacketId == PacketId.ObserversRpc)
+ // Found NetworkObject for link.
+ else if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob))
{
- Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel);
- nb.ReadObserversRpc(readerStartAfterDebug, fromRpcLink: true, link.RpcHash, reader, channel);
+ // Still call GetPacketLength to remove any extra bytes at the front of the reader.
+ NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex];
+ if (link.RpcPacketId == PacketId.TargetRpc)
+ {
+ Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel);
+ using (_pm_ParseRpcLink_TargetRpc.Auto())
+ {
+ nb.ReadTargetRpc(readerStartAfterDebug, fromRpcLink: true, link.RpcHash, reader, channel);
+ }
+ }
+ else if (link.RpcPacketId == PacketId.ObserversRpc)
+ {
+ Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel);
+ using (_pm_ParseRpcLink_ObserversRpc.Auto())
+ {
+ nb.ReadObserversRpc(readerStartAfterDebug, fromRpcLink: true, link.RpcHash, reader, channel);
+ }
+ }
+ else if (link.RpcPacketId == PacketId.Reconcile)
+ {
+ Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel);
+ using (_pm_ParseRpcLink_Reconcile.Auto())
+ {
+ nb.OnReconcileRpc(readerStartAfterDebug, link.RpcHash, reader, channel);
+ }
+ }
}
- else if (link.RpcPacketId == PacketId.Reconcile)
+ // Could not find NetworkObject.
+ else
{
- Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel);
- nb.OnReconcileRpc(readerStartAfterDebug, link.RpcHash, reader, channel);
+ dataLength = Packets.GetPacketLength(index, reader, channel);
+ SkipDataLength(index, reader, dataLength, link.ObjectId);
}
- }
- // Could not find NetworkObject.
- else
- {
- dataLength = Packets.GetPacketLength(index, reader, channel);
- SkipDataLength(index, reader, dataLength, link.ObjectId);
- }
-#if DEVELOPMENT
- NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: true, NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel);
-#endif
+ #if DEVELOPMENT
+ NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: true, NetworkManager, reader,
+ startReaderRemaining, rpcInformation, expectedReadAmount, channel);
+ #endif
+ }
}
///
@@ -97,4 +123,4 @@ internal void RemoveLinkIndexes(List values)
_rpcLinks.Remove(values[i]);
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
index 461c5418..2af8855c 100644
--- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
@@ -20,6 +20,7 @@
using FishNet.Serializing.Helping;
using UnityEngine;
using UnityEngine.SceneManagement;
+using Unity.Profiling;
namespace FishNet.Managing.Client
{
@@ -35,6 +36,27 @@ public partial class ClientObjects : ManagedObjects
private ClientObjectCache _objectCache;
#endregion
+ #region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_ParseOwnershipChange =
+ new("ClientObjects.ParseOwnershipChange(PooledReader)");
+ private static readonly ProfilerMarker _pm_ParseSyncType =
+ new("ClientObjects.ParseSyncType(PooledReader, Channel)");
+ private static readonly ProfilerMarker _pm_ParsePredictedSpawnResult =
+ new("ClientObjects.ParsePredictedSpawnResult(PooledReader)");
+ private static readonly ProfilerMarker _pm_ParseReconcileRpc =
+ new("ClientObjects.ParseReconcileRpc(PooledReader, Channel)");
+ private static readonly ProfilerMarker _pm_ParseObserversRpc =
+ new("ClientObjects.ParseObserversRpc(PooledReader, Channel)");
+ private static readonly ProfilerMarker _pm_ParseTargetRpc =
+ new("ClientObjects.ParseTargetRpc(PooledReader, Channel)");
+ private static readonly ProfilerMarker _pm_ReadSpawn =
+ new("ClientObjects.ReadSpawn(PooledReader)");
+ private static readonly ProfilerMarker _pm_CacheDespawn =
+ new("ClientObjects.CacheDespawn(PooledReader)");
+ private static readonly ProfilerMarker _pm_IterateObjectCache =
+ new("ClientObjects.IterateObjectCache()");
+ #endregion
+
internal ClientObjects(NetworkManager networkManager)
{
base.Initialize(networkManager);
@@ -99,7 +121,7 @@ internal void OnClientConnectionState(ClientConnectionStateArgs args)
/* Clear spawned and scene objects as they will be rebuilt.
* Spawned would have already be cleared if DespawnSpawned
* was called but it won't hurt anything clearing an empty collection. */
- Spawned.Clear();
+ HandleClear();
SceneObjects_Internal.Clear();
}
}
@@ -259,12 +281,15 @@ internal override void NetworkObjectDestroyed(NetworkObject nob, bool asServer)
///
internal void ParseOwnershipChange(PooledReader reader)
{
- NetworkObject nob = reader.ReadNetworkObject();
- NetworkConnection newOwner = reader.ReadNetworkConnection();
- if (nob != null && nob.IsSpawned)
- nob.GiveOwnership(newOwner, asServer: false, recursive: false);
- else
- NetworkManager.LogWarning($"NetworkBehaviour could not be found when trying to parse OwnershipChange packet.");
+ using (_pm_ParseOwnershipChange.Auto())
+ {
+ NetworkObject nob = reader.ReadNetworkObject();
+ NetworkConnection newOwner = reader.ReadNetworkConnection();
+ if (nob != null && nob.IsSpawned)
+ nob.GiveOwnership(newOwner, asServer: false, recursive: false);
+ else
+ NetworkManager.LogWarning($"NetworkBehaviour could not be found when trying to parse OwnershipChange packet.");
+ }
}
///
@@ -273,24 +298,27 @@ internal void ParseOwnershipChange(PooledReader reader)
///
internal void ParseSyncType(PooledReader reader, Channel channel)
{
- int readerPositionAfterDebug = reader.Position;
+ using (_pm_ParseSyncType.Auto())
+ {
+ int readerPositionAfterDebug = reader.Position;
- NetworkBehaviour nb = reader.ReadNetworkBehaviour();
- int length = (int)ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.SYNCTYPE_RESERVE_BYTES);
+ NetworkBehaviour nb = reader.ReadNetworkBehaviour();
+ int length = (int)ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.SYNCTYPE_RESERVE_BYTES);
- if (nb != null && nb.IsSpawned)
- {
- /* Length of data to be read for syncvars.
- * This is important because syncvars are never
- * a set length and data must be read through completion.
- * The only way to know where completion of syncvar is, versus
- * when another packet starts is by including the length. */
- if (length > 0)
- nb.ReadSyncType(readerPositionAfterDebug, reader, length);
- }
- else
- {
- SkipDataLength((ushort)PacketId.SyncType, reader, length);
+ if (nb != null && nb.IsSpawned)
+ {
+ /* Length of data to be read for syncvars.
+ * This is important because syncvars are never
+ * a set length and data must be read through completion.
+ * The only way to know where completion of syncvar is, versus
+ * when another packet starts is by including the length. */
+ if (length > 0)
+ nb.ReadSyncType(readerPositionAfterDebug, reader, length);
+ }
+ else
+ {
+ SkipDataLength((ushort)PacketId.SyncType, reader, length);
+ }
}
}
@@ -300,30 +328,33 @@ internal void ParseSyncType(PooledReader reader, Channel channel)
///
internal void ParsePredictedSpawnResult(PooledReader reader)
{
- int readerPositionAfterDebug = reader.Position;
+ using (_pm_ParsePredictedSpawnResult.Auto())
+ {
+ int readerPositionAfterDebug = reader.Position;
- bool success = reader.ReadBoolean();
- int usedObjectId = reader.ReadNetworkObjectId();
- int nextObjectId = reader.ReadNetworkObjectId();
+ bool success = reader.ReadBoolean();
+ int usedObjectId = reader.ReadNetworkObjectId();
+ int nextObjectId = reader.ReadNetworkObjectId();
- #if DEVELOPMENT && !UNITY_SERVER
- if (NetworkTrafficStatistics != null)
- NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.PredictedSpawnResult, string.Empty, reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH, gameObject: null, asServer: false);
- #endif
+ #if DEVELOPMENT && !UNITY_SERVER
+ if (NetworkTrafficStatistics != null)
+ NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.PredictedSpawnResult, string.Empty, reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH, gameObject: null, asServer: false);
+ #endif
- if (nextObjectId != NetworkObject.UNSET_OBJECTID_VALUE)
- NetworkManager.ClientManager.Connection.PredictedObjectIds.Enqueue(nextObjectId);
+ if (nextObjectId != NetworkObject.UNSET_OBJECTID_VALUE)
+ NetworkManager.ClientManager.Connection.PredictedObjectIds.Enqueue(nextObjectId);
- //Server would not allow the predicted spawn.
- if (!success)
- {
- if (Spawned.TryGetValueIL2CPP(usedObjectId, out NetworkObject nob))
+ //Server would not allow the predicted spawn.
+ if (!success)
{
- //TODO support pooling. This first requires a rework of the initialization / clientHost message system.
- nob.SetIsDestroying(DespawnType.Destroy);
- UnityEngine.Object.Destroy(nob.gameObject);
- //nob.Deinitialize(asServer: false);
- //NetworkManager.StorePooledInstantiated(nob, false);
+ if (Spawned.TryGetValueIL2CPP(usedObjectId, out NetworkObject nob))
+ {
+ //TODO support pooling. This first requires a rework of the initialization / clientHost message system.
+ nob.SetIsDestroying(DespawnType.Destroy);
+ UnityEngine.Object.Destroy(nob.gameObject);
+ //nob.Deinitialize(asServer: false);
+ //NetworkManager.StorePooledInstantiated(nob, false);
+ }
}
}
}
@@ -334,22 +365,25 @@ internal void ParsePredictedSpawnResult(PooledReader reader)
///
internal void ParseReconcileRpc(PooledReader reader, Channel channel)
{
- #if DEVELOPMENT
- NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int readerRemainingAfterLength, out string rpcInformation, out uint expectedReadAmount);
- #endif
- int readerStartAfterDebug = reader.Position;
+ using (_pm_ParseReconcileRpc.Auto())
+ {
+ #if DEVELOPMENT
+ NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int readerRemainingAfterLength, out string rpcInformation, out uint expectedReadAmount);
+ #endif
+ int readerStartAfterDebug = reader.Position;
- NetworkBehaviour nb = reader.ReadNetworkBehaviour();
- int dataLength = Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel);
+ NetworkBehaviour nb = reader.ReadNetworkBehaviour();
+ int dataLength = Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel);
- if (nb != null && nb.IsSpawned)
- nb.OnReconcileRpc(readerStartAfterDebug, hash: null, reader, channel);
- else
- SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength);
+ if (nb != null && nb.IsSpawned)
+ nb.OnReconcileRpc(readerStartAfterDebug, hash: null, reader, channel);
+ else
+ SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength);
- #if DEVELOPMENT
- NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, readerRemainingAfterLength, rpcInformation, expectedReadAmount, channel);
- #endif
+ #if DEVELOPMENT
+ NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, readerRemainingAfterLength, rpcInformation, expectedReadAmount, channel);
+ #endif
+ }
}
///
@@ -358,26 +392,29 @@ internal void ParseReconcileRpc(PooledReader reader, Channel channel)
///
internal void ParseObserversRpc(PooledReader reader, Channel channel)
{
- #if DEVELOPMENT
- NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
- #endif
- int readerStartAfterDebug = reader.Position;
-
- NetworkBehaviour nb = reader.ReadNetworkBehaviour(logException: false);
- int dataLength = Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel);
- if (nb != null && nb.IsSpawned)
+ using (_pm_ParseObserversRpc.Auto())
{
- nb.ReadObserversRpc(readerStartAfterDebug, fromRpcLink: false, hash: 0, reader, channel);
- }
- else
- {
- NetworkManager.Log($"NetworkBehaviour not found for an ObserverRpc. Rpc data will be discarded.");
- SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength);
- }
+ #if DEVELOPMENT
+ NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
+ #endif
+ int readerStartAfterDebug = reader.Position;
+
+ NetworkBehaviour nb = reader.ReadNetworkBehaviour(logException: false);
+ int dataLength = Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel);
+ if (nb != null && nb.IsSpawned)
+ {
+ nb.ReadObserversRpc(readerStartAfterDebug, fromRpcLink: false, hash: 0, reader, channel);
+ }
+ else
+ {
+ NetworkManager.Log($"NetworkBehaviour not found for an ObserverRpc. Rpc data will be discarded.");
+ SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength);
+ }
- #if DEVELOPMENT
- NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel);
- #endif
+ #if DEVELOPMENT
+ NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel);
+ #endif
+ }
}
///
@@ -386,18 +423,21 @@ internal void ParseObserversRpc(PooledReader reader, Channel channel)
///
internal void ParseTargetRpc(PooledReader reader, Channel channel)
{
- #if DEVELOPMENT
- NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
- #endif
- int readerStartAfterDebug = reader.Position;
+ using (_pm_ParseTargetRpc.Auto())
+ {
+ #if DEVELOPMENT
+ NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
+ #endif
+ int readerStartAfterDebug = reader.Position;
- NetworkBehaviour nb = reader.ReadNetworkBehaviour();
- int dataLength = Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel);
+ NetworkBehaviour nb = reader.ReadNetworkBehaviour();
+ int dataLength = Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel);
- if (nb != null && nb.IsSpawned)
- nb.ReadTargetRpc(readerStartAfterDebug, fromRpcLink: false, hash: 0, reader, channel);
- else
- SkipDataLength((ushort)PacketId.TargetRpc, reader, dataLength);
+ if (nb != null && nb.IsSpawned)
+ nb.ReadTargetRpc(readerStartAfterDebug, fromRpcLink: false, hash: 0, reader, channel);
+ else
+ SkipDataLength((ushort)PacketId.TargetRpc, reader, dataLength);
+ }
}
///
@@ -405,100 +445,107 @@ internal void ParseTargetRpc(PooledReader reader, Channel channel)
///
internal void ReadSpawn(PooledReader reader)
{
- #if DEVELOPMENT && !UNITY_SERVER
- int readerPositionAfterDebug = reader.Position;
- #endif
+ using (_pm_ReadSpawn.Auto())
+ {
+ #if DEVELOPMENT && !UNITY_SERVER
+ int readerPositionAfterDebug = reader.Position;
+ #endif
- SpawnType st = (SpawnType)reader.ReadUInt8Unpacked();
+ SpawnType st = (SpawnType)reader.ReadUInt8Unpacked();
- bool sceneObject = st.FastContains(SpawnType.Scene);
+ bool sceneObject = st.FastContains(SpawnType.Scene);
- ReadNestedSpawnIds(reader, st, out byte? nobComponentId, out int? parentObjectId, out byte? parentComponentId, _objectCache.ReadSpawningObjects);
+ ReadNestedSpawnIds(reader, st, out byte? nobComponentId, out int? parentObjectId,
+ out byte? parentComponentId, _objectCache.ReadSpawningObjects);
- //NeworkObject and owner information.
- int objectId = reader.ReadNetworkObjectForSpawn(out int initializeOrder, out ushort collectionId);
- int ownerId = reader.ReadNetworkConnectionId();
- //Read transform values which differ from serialized values.
- Vector3? localPosition;
- Quaternion? localRotation;
- Vector3? localScale;
- ReadTransformProperties(reader, out localPosition, out localRotation, out localScale);
+ //NeworkObject and owner information.
+ int objectId = reader.ReadNetworkObjectForSpawn(out int initializeOrder, out ushort collectionId);
+ int ownerId = reader.ReadNetworkConnectionId();
+ //Read transform values which differ from serialized values.
+ Vector3? localPosition;
+ Quaternion? localRotation;
+ Vector3? localScale;
+ ReadTransformProperties(reader, out localPosition, out localRotation, out localScale);
- int prefabId = 0;
- ulong sceneId = 0;
- string sceneName = string.Empty;
- string objectName = string.Empty;
+ int prefabId = 0;
+ ulong sceneId = 0;
+ string sceneName = string.Empty;
+ string objectName = string.Empty;
- if (sceneObject)
- {
- ReadSceneObjectId(reader, out sceneId);
- #if DEVELOPMENT
- if (NetworkManager.ClientManager.IsServerDevelopment)
- CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName);
- #endif
- }
- else
- {
- prefabId = reader.ReadNetworkObjectId();
- }
+ if (sceneObject)
+ {
+ ReadSceneObjectId(reader, out sceneId);
+ #if DEVELOPMENT
+ if (NetworkManager.ClientManager.IsServerDevelopment)
+ CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName);
+ #endif
+ }
+ else
+ {
+ prefabId = reader.ReadNetworkObjectId();
+ }
- ArraySegment payload = ReadPayload(reader);
- ArraySegment rpcLinks = ReadRpcLinks(reader);
- ArraySegment syncTypes = ReadSyncTypesForSpawn(reader);
+ ArraySegment payload = ReadPayload(reader);
+ ArraySegment rpcLinks = ReadRpcLinks(reader);
+ ArraySegment syncTypes = ReadSyncTypesForSpawn(reader);
- #if DEVELOPMENT && !UNITY_SERVER
- if (NetworkTrafficStatistics != null)
- NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.ObjectSpawn, string.Empty, reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH, gameObject: null, asServer: false);
- #endif
+ #if DEVELOPMENT && !UNITY_SERVER
+ if (NetworkTrafficStatistics != null)
+ NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.ObjectSpawn, string.Empty,
+ reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH,
+ gameObject: null, asServer: false);
+ #endif
- bool isPredictedSpawner = st.FastContains(SpawnType.IsPredictedSpawner);
+ bool isPredictedSpawner = st.FastContains(SpawnType.IsPredictedSpawner);
- //If found in spawn already.
- if (Spawned.TryGetValue(objectId, out NetworkObject nob))
- {
- /* If not server then extra checks must be done. Client should never
- * receive spawn messages for already spawned objects, unless they locally
- * predicted spawned the object. */
- if (!NetworkManager.IsServerStarted)
+ //If found in spawn already.
+ if (Spawned.TryGetValue(objectId, out NetworkObject nob))
{
- //Not predicted spawner.
- if (!st.FastContains(SpawnType.IsPredictedSpawner))
- {
- NetworkManager.LogWarning($"Received a spawn objectId of {objectId} which was already found in spawned, and was not predicted. This sometimes may occur on clientHost when the server destroys an object unexpectedly before the clientHost gets the spawn message.");
- }
- //Is predicted spawner.
- else
+ /* If not server then extra checks must be done. Client should never
+ * receive spawn messages for already spawned objects, unless they locally
+ * predicted spawned the object. */
+ if (!NetworkManager.IsServerStarted)
{
- PooledReader segmentReader = ReaderPool.Retrieve(ArraySegment.Empty, NetworkManager);
+ //Not predicted spawner.
+ if (!st.FastContains(SpawnType.IsPredictedSpawner))
+ {
+ NetworkManager.LogWarning($"Received a spawn objectId of {objectId} which was already found in spawned, and was not predicted. This sometimes may occur on clientHost when the server destroys an object unexpectedly before the clientHost gets the spawn message.");
+ }
+ //Is predicted spawner.
+ else
+ {
+ PooledReader segmentReader = ReaderPool.Retrieve(ArraySegment.Empty, NetworkManager);
- //RpcLinks.
- segmentReader.Initialize(rpcLinks, NetworkManager, Reader.DataSource.Server);
- ApplyRpcLinks(nob, segmentReader);
+ //RpcLinks.
+ segmentReader.Initialize(rpcLinks, NetworkManager, Reader.DataSource.Server);
+ ApplyRpcLinks(nob, segmentReader);
- //Payload.
- segmentReader.Initialize(payload, NetworkManager, Reader.DataSource.Server);
- ReadPayload(sender: null, nob, segmentReader, segmentReader.Length);
+ //Payload.
+ segmentReader.Initialize(payload, NetworkManager, Reader.DataSource.Server);
+ ReadPayload(sender: null, nob, segmentReader, segmentReader.Length);
- //SyncTypes.
- segmentReader.Initialize(syncTypes, NetworkManager, Reader.DataSource.Server);
- ApplySyncTypesForSpawn(nob, segmentReader);
- }
+ //SyncTypes.
+ segmentReader.Initialize(syncTypes, NetworkManager, Reader.DataSource.Server);
+ ApplySyncTypesForSpawn(nob, segmentReader);
+ }
- /* Nob isn't added to spawn if predicted spawner.
- * We only wanted to read and apply initial data from the server. */
- return;
+ /* Nob isn't added to spawn if predicted spawner.
+ * We only wanted to read and apply initial data from the server. */
+ return;
+ }
+ }
+ else
+ {
+ /* If predicted spawner and not in spawned then simply exit early.
+ * The predicted spawner destroyed the object locally. */
+ if (isPredictedSpawner)
+ return;
}
- }
- else
- {
- /* If predicted spawner and not in spawned then simply exit early.
- * The predicted spawner destroyed the object locally. */
- if (isPredictedSpawner)
- return;
- }
-
- _objectCache.AddSpawn(NetworkManager, collectionId, objectId, initializeOrder, ownerId, st, nobComponentId, parentObjectId, parentComponentId, prefabId, localPosition, localRotation, localScale, sceneId, sceneName, objectName, payload, rpcLinks, syncTypes);
+ _objectCache.AddSpawn(NetworkManager, collectionId, objectId, initializeOrder, ownerId, st,
+ nobComponentId, parentObjectId, parentComponentId, prefabId, localPosition, localRotation,
+ localScale, sceneId, sceneName, objectName, payload, rpcLinks, syncTypes);
+ }
}
///
@@ -507,18 +554,23 @@ internal void ReadSpawn(PooledReader reader)
///
internal void CacheDespawn(PooledReader reader)
{
- #if DEVELOPMENT && !UNITY_SERVER
- int readerPositionAfterDebug = reader.Position;
- #endif
-
- DespawnType despawnType;
- int objectId = reader.ReadNetworkObjectForDespawn(out despawnType);
- _objectCache.AddDespawn(objectId, despawnType);
-
- #if DEVELOPMENT && !UNITY_SERVER
- if (NetworkTrafficStatistics != null)
- NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.ObjectDespawn, string.Empty, reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH, gameObject: null, asServer: false);
- #endif
+ using (_pm_CacheDespawn.Auto())
+ {
+ #if DEVELOPMENT && !UNITY_SERVER
+ int readerPositionAfterDebug = reader.Position;
+ #endif
+
+ DespawnType despawnType;
+ int objectId = reader.ReadNetworkObjectForDespawn(out despawnType);
+ _objectCache.AddDespawn(objectId, despawnType);
+
+ #if DEVELOPMENT && !UNITY_SERVER
+ if (NetworkTrafficStatistics != null)
+ NetworkTrafficStatistics.AddInboundPacketIdData(PacketId.ObjectDespawn, string.Empty,
+ reader.Position - readerPositionAfterDebug + Transporting.TransportManager.PACKETID_LENGTH,
+ gameObject: null, asServer: false);
+ #endif
+ }
}
///
@@ -528,7 +580,10 @@ internal void CacheDespawn(PooledReader reader)
///
internal void IterateObjectCache()
{
- _objectCache.Iterate();
+ using (_pm_IterateObjectCache.Auto())
+ {
+ _objectCache.Iterate();
+ }
}
///
@@ -750,4 +805,4 @@ internal NetworkObject GetSpawnedNetworkObject(CachedNetworkObject cnob)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs
index 17dd0c81..87b53a15 100644
--- a/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs
@@ -501,7 +501,7 @@ internal NetworkObject GetSpawnedObject(int objectId)
//If not found in Spawning then check Spawned.
if (!IteratedSpawningObjects.TryGetValue(objectId, out result))
{
- Dictionary spawned = _networkManager.IsHostStarted ? _networkManager.ServerManager.Objects.Spawned : _networkManager.ClientManager.Objects.Spawned;
+ IReadOnlyDictionary spawned = _networkManager.IsHostStarted ? _networkManager.ServerManager.Objects.Spawned : _networkManager.ClientManager.Objects.Spawned;
spawned.TryGetValue(objectId, out result);
}
diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
index 41d9c480..f8af2dad 100644
--- a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
+++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
@@ -17,16 +17,32 @@
using FishNet.Managing.Statistic;
using UnityEngine;
using UnityEngine.SceneManagement;
+using Unity.Profiling;
namespace FishNet.Managing.Object
{
public abstract partial class ManagedObjects
{
#region Public.
+
///
/// NetworkObjects which are currently active.
///
- public Dictionary Spawned = new();
+
+ private readonly Dictionary _spawned = new();
+ public IReadOnlyDictionary Spawned => _spawned;
+
+ public delegate void OnSpawnedChanged(int objectId, NetworkObject networkObject);
+
+ public event OnSpawnedChanged OnSpawnedAdd;
+ public event OnSpawnedChanged OnSpawnedRemove;
+ public event Action OnSpawnedClear;
+
+ #endregion
+
+ #region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_ParseReplicateRpc =
+ new("ManagedObjects.ParseReplicateRpc(PooledReader, NetworkConnection, Channel)");
#endregion
#region Protected.
@@ -55,7 +71,26 @@ protected internal virtual bool GetNextNetworkObjectId(out int nextNetworkObject
public IReadOnlyDictionary SceneObjects => SceneObjects_Internal;
///
///
- protected NetworkTrafficStatistics NetworkTrafficStatistics;
+ [NonSerialized] protected NetworkTrafficStatistics NetworkTrafficStatistics;
+
+ protected void HandleAdd(NetworkObject nob)
+ {
+ _spawned[nob.ObjectId] = nob;
+ OnSpawnedAdd?.Invoke(nob.ObjectId, nob);
+ }
+
+ protected void HandleRemove(NetworkObject nob)
+ {
+ if (_spawned.Remove(nob.ObjectId))
+ OnSpawnedRemove?.Invoke(nob.ObjectId, nob);
+ }
+
+ protected void HandleClear()
+ {
+ _spawned.Clear();
+ OnSpawnedClear?.Invoke();
+ }
+
#endregion
#region Private.
@@ -109,7 +144,7 @@ internal virtual void NetworkObjectDestroyed(NetworkObject nob, bool asServer)
///
protected virtual void RemoveFromSpawned(NetworkObject nob, bool fromOnDestroy, bool asServer)
{
- Spawned.Remove(nob.ObjectId);
+ HandleRemove(nob);
// Do the same with SceneObjects.
if (fromOnDestroy && nob.IsSceneObject)
RemoveFromSceneObjects(nob);
@@ -316,7 +351,7 @@ internal virtual void DespawnWithoutSynchronization(bool recursive, bool asServe
DespawnWithoutSynchronization(nob, recursive, asServer, nob.GetDefaultDespawnType(), removeFromSpawned: false);
}
- Spawned.Clear();
+ HandleClear();
}
///
@@ -371,7 +406,7 @@ protected virtual void DespawnWithoutSynchronization(NetworkObject nob, bool rec
///
internal virtual void AddToSpawned(NetworkObject nob, bool asServer)
{
- Spawned[nob.ObjectId] = nob;
+ HandleAdd(nob);
}
///
@@ -408,7 +443,7 @@ protected internal void RemoveFromSceneObjects(ulong sceneId)
protected internal NetworkObject GetSpawnedNetworkObject(int objectId)
{
NetworkObject r;
- if (!Spawned.TryGetValueIL2CPP(objectId, out r))
+ if (!_spawned.TryGetValueIL2CPP(objectId, out r))
NetworkManager.LogError($"Spawned NetworkObject not found for ObjectId {objectId}.");
return r;
@@ -468,21 +503,26 @@ protected internal void SkipDataLength(ushort packetId, PooledReader reader, int
///
internal void ParseReplicateRpc(PooledReader reader, NetworkConnection conn, Channel channel)
{
-#if DEVELOPMENT
- NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount);
-#endif
- int readerStartAfterDebug = reader.Position;
-
- NetworkBehaviour nb = reader.ReadNetworkBehaviour();
- int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel);
- if (nb != null && nb.IsSpawned)
- nb.OnReplicateRpc(readerStartAfterDebug, hash: null, reader, conn, channel);
- else
- SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength);
+ using (_pm_ParseReplicateRpc.Auto())
+ {
+ #if DEVELOPMENT
+ NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining,
+ out string rpcInformation, out uint expectedReadAmount);
+ #endif
+ int readerStartAfterDebug = reader.Position;
+
+ NetworkBehaviour nb = reader.ReadNetworkBehaviour();
+ int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel);
+ if (nb != null && nb.IsSpawned)
+ nb.OnReplicateRpc(readerStartAfterDebug, hash: null, reader, conn, channel);
+ else
+ SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength);
-#if DEVELOPMENT
- NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel);
-#endif
+ #if DEVELOPMENT
+ NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader,
+ startReaderRemaining, rpcInformation, expectedReadAmount, channel);
+ #endif
+ }
}
#if DEVELOPMENT
@@ -512,4 +552,4 @@ protected void CheckReadSceneObjectDetails(Reader r, ref string sceneName, ref s
}
#endif
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
index 64ad0aa0..eeef7a1a 100644
--- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
@@ -178,14 +178,15 @@ public void Despawn(NetworkObject networkObject, DespawnType? despawnType = null
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
if (!conn.IsValid)
return;
OnClientKick?.Invoke(conn, conn.ClientId, kickReason);
if (conn.IsActive)
- conn.Disconnect(true);
+ conn.Disconnect(immediately);
if (!string.IsNullOrEmpty(log))
NetworkManager.Log(loggingType, log);
@@ -198,10 +199,11 @@ public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType logg
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
OnClientKick?.Invoke(null, clientId, kickReason);
- NetworkManager.TransportManager.Transport.StopConnection(clientId, true);
+ NetworkManager.TransportManager.Transport.StopConnection(clientId, immediately);
if (!string.IsNullOrEmpty(log))
NetworkManager.Log(loggingType, log);
}
@@ -214,10 +216,11 @@ public void Kick(int clientId, KickReason kickReason, LoggingType loggingType =
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
reader.Clear();
- Kick(conn, kickReason, loggingType, log);
+ Kick(conn, kickReason, loggingType, log, immediately);
}
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
index 55d879d0..78f208c1 100644
--- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
@@ -222,7 +222,7 @@ public void SetFrameRate(ushort value)
private SplitReader _splitReader = new();
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#if DEVELOPMENT
///
/// Logs data about parser to help debug.
@@ -233,6 +233,12 @@ public void SetFrameRate(ushort value)
#region Private Profiler Markers
private static readonly ProfilerMarker _pm_OnPostTick = new("ServerManager.TimeManager_OnPostTick()");
+ private static readonly ProfilerMarker _pm_Transport_OnServerConnectionState =
+ new("ServerManager.Transport_OnServerConnectionState(ServerConnectionStateArgs)");
+ private static readonly ProfilerMarker _pm_Transport_OnRemoteConnectionState =
+ new("ServerManager.Transport_OnRemoteConnectionState(RemoteConnectionStateArgs)");
+ private static readonly ProfilerMarker _pm_Transport_OnServerReceivedData =
+ new("ServerManager.Transport_OnServerReceivedData(ServerReceivedDataArgs)");
#endregion
#region Const.
@@ -522,38 +528,41 @@ private void _authenticator_OnAuthenticationResult(NetworkConnection conn, bool
///
private void Transport_OnServerConnectionState(ServerConnectionStateArgs args)
{
- /* Let the client manager know the server state is changing first.
- * This gives the client an opportunity to clean-up or prepare
- * before the server completes it's actions. */
- Started = IsAnyServerStarted();
- NetworkManager.ClientManager.Objects.OnServerConnectionState(args);
- //If no servers are started then reset data.
- if (!Started)
+ using (_pm_Transport_OnServerConnectionState.Auto())
{
- MatchCondition.StoreCollections(NetworkManager);
- //Despawn without synchronizing network objects.
- Objects.DespawnWithoutSynchronization(recursive: true, asServer: true);
- //Clear all clients.
- Clients.Clear();
- //Clients as list.
- _clientsList.Clear();
- }
- Objects.OnServerConnectionState(args);
+ /* Let the client manager know the server state is changing first.
+ * This gives the client an opportunity to clean-up or prepare
+ * before the server completes it's actions. */
+ Started = IsAnyServerStarted();
+ NetworkManager.ClientManager.Objects.OnServerConnectionState(args);
+ //If no servers are started then reset data.
+ if (!Started)
+ {
+ MatchCondition.StoreCollections(NetworkManager);
+ //Despawn without synchronizing network objects.
+ Objects.DespawnWithoutSynchronization(recursive: true, asServer: true);
+ //Clear all clients.
+ Clients.Clear();
+ //Clients as list.
+ _clientsList.Clear();
+ }
+ Objects.OnServerConnectionState(args);
- LocalConnectionState state = args.ConnectionState;
+ LocalConnectionState state = args.ConnectionState;
- if (NetworkManager.CanLog(LoggingType.Common))
- {
- Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex);
- string tName = t == null ? "Unknown" : t.GetType().Name;
- string socketInformation = string.Empty;
- if (state == LocalConnectionState.Starting)
- socketInformation = $" Listening on port {t.GetPort()}.";
- NetworkManager.Log($"Local server is {state.ToString().ToLower()} for {tName}.{socketInformation}");
- }
+ if (NetworkManager.CanLog(LoggingType.Common))
+ {
+ Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex);
+ string tName = t == null ? "Unknown" : t.GetType().Name;
+ string socketInformation = string.Empty;
+ if (state == LocalConnectionState.Starting)
+ socketInformation = $" Listening on port {t.GetPort()}.";
+ NetworkManager.Log($"Local server is {state.ToString().ToLower()} for {tName}.{socketInformation}");
+ }
- NetworkManager.UpdateFramerate();
- OnServerConnectionState?.Invoke(args);
+ NetworkManager.UpdateFramerate();
+ OnServerConnectionState?.Invoke(args);
+ }
}
///
@@ -608,47 +617,50 @@ private void ParseVersion(PooledReader reader, NetworkConnection conn, int trans
///
private void Transport_OnRemoteConnectionState(RemoteConnectionStateArgs args)
{
- //Sanity check to make sure transports are following proper types/ranges.
- int id = args.ConnectionId;
- if (id < 0 || id > NetworkConnection.MAXIMUM_CLIENTID_VALUE)
+ using (_pm_Transport_OnRemoteConnectionState.Auto())
{
- Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {NetworkConnection.MAXIMUM_CLIENTID_VALUE}. The client has been disconnected.");
- return;
- }
- //Valid Id.
- else
- {
- //If started then add to authenticated clients.
- if (args.ConnectionState == RemoteConnectionState.Started)
+ //Sanity check to make sure transports are following proper types/ranges.
+ int id = args.ConnectionId;
+ if (id < 0 || id > NetworkConnection.MAXIMUM_CLIENTID_VALUE)
{
- NetworkManager.Log($"Remote connection started for Id {id}.");
- NetworkConnection conn = new(NetworkManager, id, args.TransportIndex, true);
- Clients.Add(args.ConnectionId, conn);
- _clientsList.Add(conn);
- OnRemoteConnectionState?.Invoke(conn, args);
-
- //Do nothing else until the client sends it's version.
+ Kick(args.ConnectionId, KickReason.UnexpectedProblem, LoggingType.Error, $"The transport you are using supplied an invalid connection Id of {id}. Connection Id values must range between 0 and {NetworkConnection.MAXIMUM_CLIENTID_VALUE}. The client has been disconnected.");
+ return;
}
- //If stopping.
- else if (args.ConnectionState == RemoteConnectionState.Stopped)
+ //Valid Id.
+ else
{
- /* If client's connection is found then clean
- * them up from server. */
- if (Clients.TryGetValueIL2CPP(id, out NetworkConnection conn))
+ //If started then add to authenticated clients.
+ if (args.ConnectionState == RemoteConnectionState.Started)
{
- conn.SetDisconnecting(true);
+ NetworkManager.Log($"Remote connection started for Id {id}.");
+ NetworkConnection conn = new(NetworkManager, id, args.TransportIndex, true);
+ Clients.Add(args.ConnectionId, conn);
+ _clientsList.Add(conn);
OnRemoteConnectionState?.Invoke(conn, args);
- Clients.Remove(id);
- _clientsList.Remove(conn);
- Objects.ClientDisconnected(conn);
- BroadcastClientConnectionChange(false, conn);
- //Return predictedObjectIds.
- Queue pqId = conn.PredictedObjectIds;
- while (pqId.Count > 0)
- Objects.CacheObjectId(pqId.Dequeue());
-
- conn.ResetState();
- NetworkManager.Log($"Remote connection stopped for Id {id}.");
+
+ //Do nothing else until the client sends it's version.
+ }
+ //If stopping.
+ else if (args.ConnectionState == RemoteConnectionState.Stopped)
+ {
+ /* If client's connection is found then clean
+ * them up from server. */
+ if (Clients.TryGetValueIL2CPP(id, out NetworkConnection conn))
+ {
+ conn.SetDisconnecting(true);
+ OnRemoteConnectionState?.Invoke(conn, args);
+ Clients.Remove(id);
+ _clientsList.Remove(conn);
+ Objects.ClientDisconnected(conn);
+ BroadcastClientConnectionChange(false, conn);
+ //Return predictedObjectIds.
+ Queue pqId = conn.PredictedObjectIds;
+ while (pqId.Count > 0)
+ Objects.CacheObjectId(pqId.Dequeue());
+
+ conn.ResetState();
+ NetworkManager.Log($"Remote connection stopped for Id {id}.");
+ }
}
}
}
@@ -699,7 +711,10 @@ private void SendAuthenticated(NetworkConnection conn)
///
private void Transport_OnServerReceivedData(ServerReceivedDataArgs args)
{
- ParseReceived(args);
+ using (_pm_Transport_OnServerReceivedData.Auto())
+ {
+ ParseReceived(args);
+ }
}
///
@@ -989,4 +1004,4 @@ private void BroadcastClientConnectionChange(bool connected, NetworkConnection c
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs
index c5b2efac..77971d8f 100644
--- a/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs
+++ b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs
@@ -59,7 +59,7 @@ public override void OnInspectorGUI()
EditorGUILayout.LabelField("Physics", EditorStyles.boldLabel);
EditorGUI.indentLevel++;
if (_physicsMode.intValue == (int)PhysicsMode.TimeManager)
- EditorGUILayout.HelpBox($"Time.fixedDeltaTime will be overriden with TimeManager.TickDelta ({(1f / (float)_tickRate.intValue).ToString("0.###")})", MessageType.Info);
+ EditorGUILayout.HelpBox($"Time.fixedDeltaTime will be overridden with TimeManager.TickDelta ({(1f / (float)_tickRate.intValue).ToString("0.###")})", MessageType.Info);
else
EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events. NetworkTransform may also jitter when not using PhysicsMode.TimeManager.", MessageType.Warning);
EditorGUILayout.PropertyField(_physicsMode);
@@ -75,4 +75,5 @@ public override void OnInspectorGUI()
}
}
}
-#endif
\ No newline at end of file
+
+#endif
diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
index 931c6c63..bd4f623f 100644
--- a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
@@ -6,11 +6,9 @@
using FishNet.Transporting;
using GameKit.Dependencies.Utilities;
using System;
-using System.Collections.Generic;
using System.Runtime.CompilerServices;
-using FishNet.Managing.Predicting;
using FishNet.Managing.Statistic;
-using FishNet.Object;
+using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine;
using SystemStopwatch = System.Diagnostics.Stopwatch;
@@ -287,10 +285,12 @@ public void SetPhysicsTimeScale(float value)
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#endregion
#region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_IncreaseTick = new("TimeManager.IncreaseTick()");
+ private static readonly ProfilerMarker _pm_TryIterateData = new("TimeManager.TryIterateData(bool)");
private static readonly ProfilerMarker _pm_OnFixedUpdate = new("TimeManager.OnFixedUpdate()");
private static readonly ProfilerMarker _pm_OnPostPhysicsSimulation = new("TimeManager.OnPostPhysicsSimulation(float)");
private static readonly ProfilerMarker _pm_OnPrePhysicsSimulation = new("TimeManager.OnPrePhysicsSimulation(float)");
@@ -686,92 +686,95 @@ internal void SendPong(NetworkConnection conn, uint clientTick)
///
private void IncreaseTick()
{
- bool isClient = NetworkManager.IsClientStarted;
- bool isServer = NetworkManager.IsServerStarted;
-
- double timePerSimulation = isServer ? TickDelta : _adjustedTickDelta;
- if (timePerSimulation == 0d)
+ using (_pm_IncreaseTick.Auto())
{
- NetworkManager.LogWarning($"Simulation delta cannot be 0. Network timing will not continue.");
- return;
- }
+ bool isClient = NetworkManager.IsClientStarted;
+ bool isServer = NetworkManager.IsServerStarted;
- double time = Time.unscaledDeltaTime;
-
- _elapsedTickTime += time;
- FrameTicked = _elapsedTickTime >= timePerSimulation;
+ double timePerSimulation = isServer ? TickDelta : _adjustedTickDelta;
+ if (timePerSimulation == 0d)
+ {
+ NetworkManager.LogWarning($"Simulation delta cannot be 0. Network timing will not continue.");
+ return;
+ }
- // Number of ticks to occur this frame.
- int ticksCount = Mathf.FloorToInt((float)(_elapsedTickTime / timePerSimulation));
- if (ticksCount > 1)
- _lastMultipleTicksTime = Time.unscaledTime;
+ double time = Time.unscaledDeltaTime;
- if (_allowTickDropping)
- {
- // If ticks require dropping. Set exactly to maximum ticks.
- if (ticksCount > _maximumFrameTicks)
- _elapsedTickTime = timePerSimulation * (double)_maximumFrameTicks;
- }
+ _elapsedTickTime += time;
+ FrameTicked = _elapsedTickTime >= timePerSimulation;
- bool variableTiming = _timingType == TimingType.Variable;
- bool frameTicked = FrameTicked;
- float tickDelta = (float)TickDelta * GetPhysicsTimeScale();
+ // Number of ticks to occur this frame.
+ int ticksCount = Mathf.FloorToInt((float)(_elapsedTickTime / timePerSimulation));
+ if (ticksCount > 1)
+ _lastMultipleTicksTime = Time.unscaledTime;
- do
- {
- if (frameTicked)
+ if (_allowTickDropping)
{
- using (_pm_OnPreTick.Auto())
- OnPreTick?.Invoke();
+ // If ticks require dropping. Set exactly to maximum ticks.
+ if (ticksCount > _maximumFrameTicks)
+ _elapsedTickTime = timePerSimulation * (double)_maximumFrameTicks;
}
- /* This has to be called inside the loop because
- * OnPreTick promises data hasn't been read yet.
- * Therefor iterate must occur after OnPreTick.
- * Iteration will only run once per frame. */
- if (frameTicked || variableTiming)
- TryIterateData(true);
+ bool variableTiming = _timingType == TimingType.Variable;
+ bool frameTicked = FrameTicked;
+ float tickDelta = (float)TickDelta * GetPhysicsTimeScale();
- if (frameTicked)
+ do
{
- // Tell predicted objecs to reconcile before OnTick.
- NetworkManager.PredictionManager.ReconcileToStates();
+ if (frameTicked)
+ {
+ using (_pm_OnPreTick.Auto())
+ OnPreTick?.Invoke();
+ }
- using (_pm_OnTick.Auto())
- OnTick?.Invoke();
+ /* This has to be called inside the loop because
+ * OnPreTick promises data hasn't been read yet.
+ * Therefor iterate must occur after OnPreTick.
+ * Iteration will only run once per frame. */
+ if (frameTicked || variableTiming)
+ TryIterateData(true);
- if (PhysicsMode == PhysicsMode.TimeManager && tickDelta > 0f)
+ if (frameTicked)
{
- InvokeOnSimulation(preSimulation: true, tickDelta);
- SimulatePhysics(tickDelta);
- InvokeOnSimulation(preSimulation: false, tickDelta);
+ // Tell predicted objecs to reconcile before OnTick.
+ NetworkManager.PredictionManager.ReconcileToStates();
+
+ using (_pm_OnTick.Auto())
+ OnTick?.Invoke();
+
+ if (PhysicsMode == PhysicsMode.TimeManager && tickDelta > 0f)
+ {
+ InvokeOnSimulation(preSimulation: true, tickDelta);
+ SimulatePhysics(tickDelta);
+ InvokeOnSimulation(preSimulation: false, tickDelta);
+ }
+
+ using (_pm_OnPostTick.Auto())
+ OnPostTick?.Invoke();
+ // After post tick send states.
+ NetworkManager.PredictionManager.SendStateUpdate();
+
+ /* If isClient this is the
+ * last tick during this loop. */
+ bool lastTick = _elapsedTickTime < timePerSimulation * 2d;
+ if (isClient && lastTick)
+ TrySendPing(LocalTick + 1);
+ if (NetworkManager.IsServerStarted)
+ SendTimingAdjustment();
}
- using (_pm_OnPostTick.Auto())
- OnPostTick?.Invoke();
- // After post tick send states.
- NetworkManager.PredictionManager.SendStateUpdate();
-
- /* If isClient this is the
- * last tick during this loop. */
- bool lastTick = _elapsedTickTime < timePerSimulation * 2d;
- if (isClient && lastTick)
- TrySendPing(LocalTick + 1);
- if (NetworkManager.IsServerStarted)
- SendTimingAdjustment();
- }
+ // Send out data.
+ if (frameTicked || variableTiming)
+ TryIterateData(false);
- // Send out data.
- if (frameTicked || variableTiming)
- TryIterateData(false);
-
- if (frameTicked)
- {
- _elapsedTickTime -= timePerSimulation;
- Tick++;
- LocalTick++;
- }
- } while (_elapsedTickTime >= timePerSimulation);
+ if (frameTicked)
+ {
+ _elapsedTickTime -= timePerSimulation;
+ Tick++;
+ LocalTick++;
+ }
+ } while (_elapsedTickTime >= timePerSimulation);
+ }
}
#region Tick conversions.
@@ -793,12 +796,14 @@ public double GetTickPercentAsDouble()
/// Returns the current elapsed amount for the next tick.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public double GetTickElapsedAsDouble() => _elapsedTickTime;
///
/// Returns the percentage of how far the TimeManager is into the next tick.
/// Value will return between 0 and 100.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetTickPercentAsByte()
{
double result = GetTickPercentAsDouble();
@@ -809,6 +814,7 @@ public byte GetTickPercentAsByte()
/// Converts a 0 to 100 byte value to a 0d to 1d percent value.
/// This does not check for excessive byte values, such as anything over 100.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double GetTickPercentAsDouble(byte value)
{
return value / 100d;
@@ -890,6 +896,7 @@ public double TicksToTime(TickType tickType = TickType.LocalTick)
///
/// PreciseTick to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public double TicksToTime(PreciseTick pt)
{
double tickTime = TicksToTime(pt.Tick);
@@ -902,6 +909,7 @@ public double TicksToTime(PreciseTick pt)
///
/// Ticks to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public double TicksToTime(uint ticks)
{
return TickDelta * (double)ticks;
@@ -991,16 +999,28 @@ public double TimePassed(uint previousTick, bool allowNegative = false)
///
/// Time to convert as decimal.
///
- public uint TimeToTicks(double time, TickRounding rounding = TickRounding.RoundNearest)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint TimeToTicks(double time, double tickDelta, TickRounding rounding = TickRounding.RoundNearest)
{
- double result = time / TickDelta;
+ double result = time / tickDelta;
if (rounding == TickRounding.RoundNearest)
- return (uint)Math.Round(result);
+ return (uint)math.round(result);
else if (rounding == TickRounding.RoundDown)
- return (uint)Math.Floor(result);
+ return (uint)math.floor(result);
else
- return (uint)Math.Ceiling(result);
+ return (uint)math.ceil(result);
+ }
+
+ ///
+ /// Converts time to ticks.
+ ///
+ /// Time to convert as decimal.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public uint TimeToTicks(double time, TickRounding rounding = TickRounding.RoundNearest)
+ {
+ return TimeToTicks(time, TickDelta, rounding);
}
///
@@ -1008,6 +1028,7 @@ public uint TimeToTicks(double time, TickRounding rounding = TickRounding.RoundN
///
/// Time to convert as whole (milliseconds)
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint TimeToTicks(long time, TickRounding rounding = TickRounding.RoundNearest)
{
double dTime = (double)time / 1000d;
@@ -1019,6 +1040,7 @@ public uint TimeToTicks(long time, TickRounding rounding = TickRounding.RoundNea
///
/// Time to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public PreciseTick TimeToPreciseTick(double time) => time.AsPreciseTick(TickDelta);
///
@@ -1086,35 +1108,38 @@ internal void SimulatePhysics(float delta)
using (_pm_Physics2DSimulate.Auto())
Physics2D.Simulate(delta);
}
-
+
///
/// Tries to iterate incoming or outgoing data.
///
/// True to iterate incoming.
private void TryIterateData(bool incoming)
{
- if (incoming)
+ using (_pm_TryIterateData.Auto())
{
- /* It's not possible for data to come in
- * more than once per frame but there could
- * be new data going out each tick, since
- * movement is often based off the tick system.
- * Because of this don't iterate incoming if
- * it's the same frame, but the outgoing
- * may iterate multiple times per frame due to
- * there possibly being multiple ticks per frame. */
- int frameCount = Time.frameCount;
- if (frameCount == _lastIncomingIterationFrame)
- return;
- _lastIncomingIterationFrame = frameCount;
-
- NetworkManager.TransportManager.IterateIncoming(asServer: true);
- NetworkManager.TransportManager.IterateIncoming(asServer: false);
- }
- else
- {
- NetworkManager.TransportManager.IterateOutgoing(asServer: true);
- NetworkManager.TransportManager.IterateOutgoing(asServer: false);
+ if (incoming)
+ {
+ /* It's not possible for data to come in
+ * more than once per frame but there could
+ * be new data going out each tick, since
+ * movement is often based off the tick system.
+ * Because of this don't iterate incoming if
+ * it's the same frame, but the outgoing
+ * may iterate multiple times per frame due to
+ * there possibly being multiple ticks per frame. */
+ int frameCount = Time.frameCount;
+ if (frameCount == _lastIncomingIterationFrame)
+ return;
+ _lastIncomingIterationFrame = frameCount;
+
+ NetworkManager.TransportManager.IterateIncoming(asServer: true);
+ NetworkManager.TransportManager.IterateIncoming(asServer: false);
+ }
+ else
+ {
+ NetworkManager.TransportManager.IterateOutgoing(asServer: true);
+ NetworkManager.TransportManager.IterateOutgoing(asServer: false);
+ }
}
}
diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
index d1ca6a57..3e2bf1e3 100644
--- a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
@@ -11,6 +11,7 @@
using System.Collections.Generic;
using FishNet.Managing.Statistic;
using GameKit.Dependencies.Utilities;
+using Unity.Profiling;
using UnityEngine;
namespace FishNet.Managing.Transporting
@@ -122,7 +123,12 @@ public LatencySimulator LatencySimulator
private int _customMtuReserve = MINIMUM_MTU_RESERVE;
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
+ #endregion
+
+ #region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_IterateIncoming = new("TimeManager.IterateIncoming(bool)");
+ private static readonly ProfilerMarker _pm_IterateOutgoing = new("TimeManager.IterateOutgoing(bool)");
#endregion
#region Consts.
@@ -746,9 +752,12 @@ private void SendSplitData(NetworkConnection conn, ref ArraySegment segmen
/// True to read data from clients, false to read data from the server.
internal void IterateIncoming(bool asServer)
{
- OnIterateIncomingStart?.Invoke(asServer);
- Transport.IterateIncoming(asServer);
- OnIterateIncomingEnd?.Invoke(asServer);
+ using (_pm_IterateIncoming.Auto())
+ {
+ OnIterateIncomingStart?.Invoke(asServer);
+ Transport.IterateIncoming(asServer);
+ OnIterateIncomingEnd?.Invoke(asServer);
+ }
}
///
@@ -757,156 +766,159 @@ internal void IterateIncoming(bool asServer)
/// True to send data from the local server to clients, false to send from the local client to server.
internal void IterateOutgoing(bool asServer)
{
- if (asServer && _networkManager.ServerManager.AreAllServersStopped())
- return;
+ using (_pm_IterateOutgoing.Auto())
+ {
+ if (asServer && _networkManager.ServerManager.AreAllServersStopped())
+ return;
- OnIterateOutgoingStart?.Invoke();
- int channelCount = CHANNEL_COUNT;
- ulong sentBytes = 0;
+ OnIterateOutgoingStart?.Invoke();
+ int channelCount = CHANNEL_COUNT;
+ ulong sentBytes = 0;
#if DEVELOPMENT
- bool latencySimulatorEnabled = LatencySimulator.CanSimulate;
+ bool latencySimulatorEnabled = LatencySimulator.CanSimulate;
#endif
- if (asServer)
- SendAsServer();
- else
- SendAsClient();
-
- // Sends data as server.
- void SendAsServer()
- {
- TimeManager tm = _networkManager.TimeManager;
- uint localTick = tm.LocalTick;
- // Write any dirty syncTypes.
- _networkManager.ServerManager.Objects.WriteDirtySyncTypes();
-
- int dirtyCount = _dirtyToClients.Count;
+ if (asServer)
+ SendAsServer();
+ else
+ SendAsClient();
- // Run through all dirty connections to send data to.
- for (int z = 0; z < dirtyCount; z++)
+ // Sends data as server.
+ void SendAsServer()
{
- NetworkConnection conn = _dirtyToClients[z];
- if (conn == null || !conn.IsValid)
- continue;
+ TimeManager tm = _networkManager.TimeManager;
+ uint localTick = tm.LocalTick;
+ // Write any dirty syncTypes.
+ _networkManager.ServerManager.Objects.WriteDirtySyncTypes();
- // Get packets for every channel.
- for (byte channel = 0; channel < channelCount; channel++)
+ int dirtyCount = _dirtyToClients.Count;
+
+ // Run through all dirty connections to send data to.
+ for (int z = 0; z < dirtyCount; z++)
{
- if (conn.GetPacketBundle(channel, out PacketBundle pb))
- {
- ProcessPacketBundle(pb);
- ProcessPacketBundle(pb.GetSendLastBundle(), true);
+ NetworkConnection conn = _dirtyToClients[z];
+ if (conn == null || !conn.IsValid)
+ continue;
- void ProcessPacketBundle(PacketBundle ppb, bool isLast = false)
+ // Get packets for every channel.
+ for (byte channel = 0; channel < channelCount; channel++)
+ {
+ if (conn.GetPacketBundle(channel, out PacketBundle pb))
{
- for (int i = 0; i < ppb.WrittenBuffers; i++)
+ ProcessPacketBundle(pb);
+ ProcessPacketBundle(pb.GetSendLastBundle(), true);
+
+ void ProcessPacketBundle(PacketBundle ppb, bool isLast = false)
{
- // Length should always be more than 0 but check to be safe.
- if (ppb.GetBuffer(i, out ByteBuffer bb))
+ for (int i = 0; i < ppb.WrittenBuffers; i++)
{
- ArraySegment segment = new(bb.Data, 0, bb.Length);
- if (HasIntermediateLayer)
- segment = ProcessIntermediateOutgoing(segment, false);
+ // Length should always be more than 0 but check to be safe.
+ if (ppb.GetBuffer(i, out ByteBuffer bb))
+ {
+ ArraySegment segment = new(bb.Data, 0, bb.Length);
+ if (HasIntermediateLayer)
+ segment = ProcessIntermediateOutgoing(segment, false);
#if DEVELOPMENT
- if (latencySimulatorEnabled)
- _latencySimulator.AddOutgoing(channel, segment, false, conn.ClientId);
- else
+ if (latencySimulatorEnabled)
+ _latencySimulator.AddOutgoing(channel, segment, false, conn.ClientId);
+ else
#endif
- Transport.SendToClient(channel, segment, conn.ClientId);
- sentBytes += (ulong)segment.Count;
+ Transport.SendToClient(channel, segment, conn.ClientId);
+ sentBytes += (ulong)segment.Count;
+ }
}
- }
- ppb.Reset(false);
+ ppb.Reset(false);
+ }
}
}
- }
- /* When marked as disconnecting data will still be sent
- * this iteration but the connection will be marked as invalid.
- * This will prevent future data from going out/coming in.
- * Also the connection will be added to a disconnecting collection
- * so it will it disconnected briefly later to allow data from
- * this tick to send. */
- if (conn.Disconnecting)
- {
- uint requiredTicks = tm.TimeToTicks(0.1d, TickRounding.RoundUp);
- /* Require 100ms or 2 ticks to pass
- * before disconnecting to allow for the
- * higher chance of success that remaining
- * data is sent. */
- requiredTicks = Math.Max(requiredTicks, 2);
- _disconnectingClients.Add(new(requiredTicks + localTick, conn));
- }
+ /* When marked as disconnecting data will still be sent
+ * this iteration but the connection will be marked as invalid.
+ * This will prevent future data from going out/coming in.
+ * Also the connection will be added to a disconnecting collection
+ * so it will it disconnected briefly later to allow data from
+ * this tick to send. */
+ if (conn.Disconnecting)
+ {
+ uint requiredTicks = tm.TimeToTicks(0.1d, TickRounding.RoundUp);
+ /* Require 100ms or 2 ticks to pass
+ * before disconnecting to allow for the
+ * higher chance of success that remaining
+ * data is sent. */
+ requiredTicks = Math.Max(requiredTicks, 2);
+ _disconnectingClients.Add(new(requiredTicks + localTick, conn));
+ }
- conn.ResetServerDirty();
- }
+ conn.ResetServerDirty();
+ }
- // Iterate disconnects.
- for (int i = 0; i < _disconnectingClients.Count; i++)
- {
- DisconnectingClient dc = _disconnectingClients[i];
- if (localTick >= dc.Tick)
+ // Iterate disconnects.
+ for (int i = 0; i < _disconnectingClients.Count; i++)
{
- _networkManager.TransportManager.Transport.StopConnection(dc.Connection.ClientId, true);
- _disconnectingClients.RemoveAt(i);
- i--;
+ DisconnectingClient dc = _disconnectingClients[i];
+ if (localTick >= dc.Tick)
+ {
+ _networkManager.TransportManager.Transport.StopConnection(dc.Connection.ClientId, true);
+ _disconnectingClients.RemoveAt(i);
+ i--;
+ }
}
- }
- if (_networkTrafficStatistics != null)
- _networkTrafficStatistics.AddOutboundSocketData(sentBytes, asServer: true);
+ if (_networkTrafficStatistics != null)
+ _networkTrafficStatistics.AddOutboundSocketData(sentBytes, asServer: true);
- if (dirtyCount == _dirtyToClients.Count)
- _dirtyToClients.Clear();
- else if (dirtyCount > 0)
- _dirtyToClients.RemoveRange(0, dirtyCount);
- }
+ if (dirtyCount == _dirtyToClients.Count)
+ _dirtyToClients.Clear();
+ else if (dirtyCount > 0)
+ _dirtyToClients.RemoveRange(0, dirtyCount);
+ }
- // Sends data as client.
- void SendAsClient()
- {
- for (byte channel = 0; channel < channelCount; channel++)
+ // Sends data as client.
+ void SendAsClient()
{
- if (PacketBundle.GetPacketBundle(channel, _toServerBundles, out PacketBundle pb))
+ for (byte channel = 0; channel < channelCount; channel++)
{
- ProcessPacketBundle(pb);
- ProcessPacketBundle(pb.GetSendLastBundle());
-
- void ProcessPacketBundle(PacketBundle ppb)
+ if (PacketBundle.GetPacketBundle(channel, _toServerBundles, out PacketBundle pb))
{
- for (int i = 0; i < ppb.WrittenBuffers; i++)
+ ProcessPacketBundle(pb);
+ ProcessPacketBundle(pb.GetSendLastBundle());
+
+ void ProcessPacketBundle(PacketBundle ppb)
{
- if (ppb.GetBuffer(i, out ByteBuffer bb))
+ for (int i = 0; i < ppb.WrittenBuffers; i++)
{
- ArraySegment segment = new(bb.Data, 0, bb.Length);
- if (HasIntermediateLayer)
- segment = ProcessIntermediateOutgoing(segment, true);
+ if (ppb.GetBuffer(i, out ByteBuffer bb))
+ {
+ ArraySegment segment = new(bb.Data, 0, bb.Length);
+ if (HasIntermediateLayer)
+ segment = ProcessIntermediateOutgoing(segment, true);
#if DEVELOPMENT
- if (latencySimulatorEnabled)
- _latencySimulator.AddOutgoing(channel, segment);
- else
+ if (latencySimulatorEnabled)
+ _latencySimulator.AddOutgoing(channel, segment);
+ else
#endif
- Transport.SendToServer(channel, segment);
- sentBytes += (ulong)segment.Count;
+ Transport.SendToServer(channel, segment);
+ sentBytes += (ulong)segment.Count;
+ }
}
- }
- ppb.Reset(false);
+ ppb.Reset(false);
+ }
}
}
- }
- if (_networkTrafficStatistics != null)
- _networkTrafficStatistics.AddOutboundSocketData(sentBytes, asServer: false);
- }
+ if (_networkTrafficStatistics != null)
+ _networkTrafficStatistics.AddOutboundSocketData(sentBytes, asServer: false);
+ }
#if DEVELOPMENT
- if (latencySimulatorEnabled)
- _latencySimulator.IterateOutgoing(asServer);
+ if (latencySimulatorEnabled)
+ _latencySimulator.IterateOutgoing(asServer);
#endif
- Transport.IterateOutgoing(asServer);
- OnIterateOutgoingEnd?.Invoke();
+ Transport.IterateOutgoing(asServer);
+ OnIterateOutgoingEnd?.Invoke();
+ }
}
#region Editor.
diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs
index ddcfbbfa..d3e73e31 100644
--- a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs
@@ -1,8 +1,11 @@
-using FishNet.Connection;
+using System;
+using FishNet.Connection;
using FishNet.Documenting;
using FishNet.Object.Synchronizing.Internal;
using FishNet.Serializing;
using System.Runtime.CompilerServices;
+using FishNet.Managing;
+using Unity.Profiling;
using UnityEngine;
namespace FishNet.Object
@@ -23,6 +26,24 @@ public abstract partial class NetworkBehaviour : MonoBehaviour
#endregion
#region Private.
+
+ #region Private Profiler Markers
+ private static readonly ProfilerMarker _pm_InvokeSyncTypeOnStartCallbacks = new("NetworkBehaviour.InvokeSyncTypeOnStartCallbacks(bool)");
+ private static readonly ProfilerMarker _pm_InvokeSyncTypeOnStopCallbacks = new("NetworkBehaviour.InvokeSyncTypeOnStopCallbacks(bool)");
+
+ private static readonly ProfilerMarker _pm_InvokeOnNetwork_Internal = new("NetworkBehaviour.InvokeOnNetwork_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnStartNetwork_Internal = new("NetworkBehaviour.OnStartNetwork_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnStopNetwork_Internal = new("NetworkBehaviour.OnStopNetwork_Internal(bool)");
+
+ private static readonly ProfilerMarker _pm_OnStartServer_Internal = new("NetworkBehaviour.OnStartServer_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnStopServer_Internal = new("NetworkBehaviour.OnStopServer_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnOwnershipServer_Internal = new("NetworkBehaviour.OnOwnershipServer_Internal(NetworkConnection)");
+
+ private static readonly ProfilerMarker _pm_OnStartClient_Internal = new("NetworkBehaviour.OnStartClient_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnStopClient_Internal = new("NetworkBehaviour.OnStopClient_Internal(bool)");
+ private static readonly ProfilerMarker _pm_OnOwnershipClient_Internal = new("NetworkBehaviour.OnOwnershipClient_Internal(NetworkConnection)");
+ #endregion
+
///
/// True if OnStartNetwork has been called.
///
@@ -51,8 +72,20 @@ public virtual void ReadPayload(NetworkConnection connection, Reader reader) { }
///
internal void InvokeSyncTypeOnStartCallbacks(bool asServer)
{
- foreach (SyncBase item in _syncTypes.Values)
- item.OnStartCallback(asServer);
+ using (_pm_InvokeSyncTypeOnStartCallbacks.Auto())
+ {
+ foreach (SyncBase item in _syncTypes.Values)
+ {
+ try
+ {
+ item.OnStartCallback(asServer);
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
+ }
}
///
@@ -60,10 +93,22 @@ internal void InvokeSyncTypeOnStartCallbacks(bool asServer)
///
internal void InvokeSyncTypeOnStopCallbacks(bool asServer)
{
- // if (_syncTypes == null)
- // return;
- foreach (SyncBase item in _syncTypes.Values)
- item.OnStopCallback(asServer);
+ using (_pm_InvokeSyncTypeOnStopCallbacks.Auto())
+ {
+ // if (_syncTypes == null)
+ // return;
+ foreach (SyncBase item in _syncTypes.Values)
+ {
+ try
+ {
+ item.OnStopCallback(asServer);
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
+ }
}
///
@@ -71,31 +116,45 @@ internal void InvokeSyncTypeOnStopCallbacks(bool asServer)
///
internal void InvokeOnNetwork_Internal(bool start)
{
- if (start)
+ using (_pm_InvokeOnNetwork_Internal.Auto())
{
- if (_onStartNetworkCalled)
- return;
+ if (start)
+ {
+ if (_onStartNetworkCalled)
+ return;
- if (!gameObject.activeInHierarchy)
+ if (!gameObject.activeInHierarchy)
+ {
+ NetworkInitialize___Early();
+ NetworkInitialize___Late();
+ }
+
+ OnStartNetwork_Internal();
+ }
+ else
{
- NetworkInitialize___Early();
- NetworkInitialize___Late();
+ if (_onStopNetworkCalled)
+ return;
+ OnStopNetwork_Internal();
}
- OnStartNetwork_Internal();
- }
- else
- {
- if (_onStopNetworkCalled)
- return;
- OnStopNetwork_Internal();
}
}
internal virtual void OnStartNetwork_Internal()
{
- _onStartNetworkCalled = true;
- _onStopNetworkCalled = false;
- OnStartNetwork();
+ using (_pm_OnStartNetwork_Internal.Auto())
+ {
+ _onStartNetworkCalled = true;
+ _onStopNetworkCalled = false;
+ try
+ {
+ OnStartNetwork();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -107,10 +166,20 @@ public virtual void OnStartNetwork() { }
internal virtual void OnStopNetwork_Internal()
{
- _onStopNetworkCalled = true;
- _onStartNetworkCalled = false;
+ using (_pm_OnStopNetwork_Internal.Auto())
+ {
+ _onStopNetworkCalled = true;
+ _onStartNetworkCalled = false;
- OnStopNetwork();
+ try
+ {
+ OnStopNetwork();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -122,8 +191,18 @@ public virtual void OnStopNetwork() { }
internal void OnStartServer_Internal()
{
- OnStartServerCalled = true;
- OnStartServer();
+ using (_pm_OnStartServer_Internal.Auto())
+ {
+ OnStartServerCalled = true;
+ try
+ {
+ OnStartServer();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -134,9 +213,19 @@ public virtual void OnStartServer() { }
internal void OnStopServer_Internal()
{
- OnStartServerCalled = false;
- ReturnRpcLinks();
- OnStopServer();
+ using (_pm_OnStopServer_Internal.Auto())
+ {
+ OnStartServerCalled = false;
+ ReturnRpcLinks();
+ try
+ {
+ OnStopServer();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -146,8 +235,18 @@ public virtual void OnStopServer() { }
internal void OnOwnershipServer_Internal(NetworkConnection prevOwner)
{
- ResetState_Prediction(true);
- OnOwnershipServer(prevOwner);
+ using (_pm_OnOwnershipServer_Internal.Auto())
+ {
+ ResetState_Prediction(true);
+ try
+ {
+ OnOwnershipServer(prevOwner);
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -171,8 +270,18 @@ public virtual void OnDespawnServer(NetworkConnection connection) { }
internal void OnStartClient_Internal()
{
- OnStartClientCalled = true;
- OnStartClient();
+ using (_pm_OnStartClient_Internal.Auto())
+ {
+ OnStartClientCalled = true;
+ try
+ {
+ OnStartClient();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -182,8 +291,18 @@ public virtual void OnStartClient() { }
internal void OnStopClient_Internal()
{
- OnStartClientCalled = false;
- OnStopClient();
+ using (_pm_OnStopClient_Internal.Auto())
+ {
+ OnStartClientCalled = false;
+ try
+ {
+ OnStopClient();
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
@@ -193,13 +312,23 @@ public virtual void OnStopClient() { }
internal void OnOwnershipClient_Internal(NetworkConnection prevOwner)
{
- // If losing or gaining ownership then clear replicate cache.
- if (IsOwner || prevOwner == LocalConnection)
+ using (_pm_OnOwnershipClient_Internal.Auto())
{
- ResetState_Prediction(false);
- }
+ // If losing or gaining ownership then clear replicate cache.
+ if (IsOwner || prevOwner == LocalConnection)
+ {
+ ResetState_Prediction(false);
+ }
- OnOwnershipClient(prevOwner);
+ try
+ {
+ OnOwnershipClient(prevOwner);
+ }
+ catch (Exception e)
+ {
+ NetworkManager.LogError(e.ToString());
+ }
+ }
}
///
diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs
index b08be1e8..44867ace 100644
--- a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs
@@ -101,7 +101,7 @@ public byte ComponentIndex
///
public override string ToString()
{
- return $"Name [{gameObject.name}] ComponentId [{ComponentIndex}] NetworkObject Name [{_networkObjectCache.name}] NetworkObject Id [{_networkObjectCache.ObjectId}]";
+ return $"Name [{gameObject.name}] ComponentId [{ComponentIndex}] NetworkObject Name [{_networkObjectCache?.name ?? string.Empty}] NetworkObject Id [{_networkObjectCache?.ObjectId ?? -1}]";
}
[MakePublic]
diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs
index 0c6c2e57..f8a12645 100644
--- a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs
@@ -1,12 +1,22 @@
using FishNet.Connection;
using System.Runtime.CompilerServices;
+using System;
using FishNet.Serializing;
using UnityEngine;
+using Unity.Profiling;
namespace FishNet.Object
{
public partial class NetworkObject : MonoBehaviour
{
+ #region Types
+
+ public delegate void NetworkObjectCallback(NetworkObject nb);
+
+ public delegate void NetworkObjectInvokeCallback(NetworkObject nb, bool asServer, bool invokeSyncTypeCallbacks);
+
+ #endregion
+
#region Private.
///
/// True if OnStartServer was called.
@@ -16,6 +26,133 @@ public partial class NetworkObject : MonoBehaviour
/// True if OnStartClient was called.
///
private bool _onStartClientCalled;
+ ///
+ /// True if OnStartSyncTypeCallbacks was called.
+ ///
+ private bool _onStartSyncTypeCallbacksCalled;
+
+ ///
+ /// True if OnStartServer was called.
+ ///
+ public bool OnStartServerCalled
+ {
+ get => _onStartServerCalled;
+ private set
+ {
+ if (_onStartServerCalled != value)
+ {
+ _onStartServerCalled = value;
+ if (value)
+ {
+ using (_pm_OnStartServerEvent.Auto())
+ OnStartServerEvent?.Invoke(this);
+ }
+ else
+ {
+ using (_pm_OnStopServerEvent.Auto())
+ OnStopServerEvent?.Invoke(this);
+ }
+ }
+ }
+ }
+
+ ///
+ /// True if OnStartClient was called.
+ ///
+ public bool OnStartClientCalled
+ {
+ get => _onStartClientCalled;
+ private set
+ {
+ if (_onStartClientCalled != value)
+ {
+ _onStartClientCalled = value;
+ if (value)
+ {
+ using (_pm_OnStartClientEvent.Auto())
+ OnStartClientEvent?.Invoke(this);
+ }
+ else
+ {
+ using (_pm_OnStopClientEvent.Auto())
+ OnStopClientEvent?.Invoke(this);
+ }
+ }
+ }
+ }
+
+ ///
+ /// True if OnStartSyncTypeCallbacks was called.
+ ///
+ public bool OnStartSyncTypeCallbacksCalled
+ {
+ get => _onStartSyncTypeCallbacksCalled;
+ private set
+ {
+ if (_onStartSyncTypeCallbacksCalled != value)
+ {
+ _onStartSyncTypeCallbacksCalled = value;
+ if (value)
+ {
+ using (_pm_OnStartSyncTypeCallbacksEvent.Auto())
+ OnStartSyncTypeCallbacks?.Invoke(this);
+ }
+ else
+ {
+ using (_pm_OnStopSyncTypeCallbacksEvent.Auto())
+ OnStopSyncTypeCallbacks?.Invoke(this);
+ }
+ }
+ }
+ }
+
+ public event NetworkObjectCallback OnStartServerEvent;
+ public event NetworkObjectCallback OnStopServerEvent;
+ public event NetworkObjectCallback OnStartClientEvent;
+ public event NetworkObjectCallback OnStopClientEvent;
+ public event NetworkObjectCallback OnStartSyncTypeCallbacks;
+ public event NetworkObjectCallback OnStopSyncTypeCallbacks;
+ public event NetworkObjectCallback OnServerInitializedEvent;
+ public event NetworkObjectCallback OnClientInitializedEvent;
+ public event NetworkObjectCallback OnServerDeinitializedEvent;
+ public event NetworkObjectCallback OnClientDeinitializedEvent;
+ public event NetworkObjectInvokeCallback PreInvokeStartCallbacks;
+ public event NetworkObjectInvokeCallback PostInvokeStartCallbacks;
+ public event NetworkObjectInvokeCallback PreInvokeStopCallbacks;
+ public event NetworkObjectInvokeCallback PostInvokeStopCallbacks;
+
+ #region Profiling.
+ private static readonly ProfilerMarker _pm_OnStartServerEvent =
+ new("NetworkObject.OnStartServerEvent");
+ private static readonly ProfilerMarker _pm_OnStopServerEvent =
+ new("NetworkObject.OnStopServerEvent");
+ private static readonly ProfilerMarker _pm_OnStartClientEvent =
+ new("NetworkObject.OnStartClientEvent");
+ private static readonly ProfilerMarker _pm_OnStopClientEvent =
+ new("NetworkObject.OnStopClientEvent");
+ private static readonly ProfilerMarker _pm_OnStartSyncTypeCallbacksEvent =
+ new("NetworkObject.OnStartSyncTypeCallbacks");
+ private static readonly ProfilerMarker _pm_OnStopSyncTypeCallbacksEvent =
+ new("NetworkObject.OnStopSyncTypeCallbacks");
+ private static readonly ProfilerMarker _pm_OnServerInitializedEvent =
+ new("NetworkObject.OnServerInitializedEvent");
+ private static readonly ProfilerMarker _pm_OnClientInitializedEvent =
+ new("NetworkObject.OnClientInitializedEvent");
+ private static readonly ProfilerMarker _pm_OnServerDeinitializedEvent =
+ new("NetworkObject.OnServerDeinitializedEvent");
+ private static readonly ProfilerMarker _pm_OnClientDeinitializedEvent =
+ new("NetworkObject.OnClientDeinitializedEvent");
+ private static readonly ProfilerMarker _pm_PreInvokeStartCallbacksEvent =
+ new("NetworkObject.PreInvokeStartCallbacks");
+ private static readonly ProfilerMarker _pm_PostInvokeStartCallbacksEvent =
+ new("NetworkObject.PostInvokeStartCallbacks");
+ private static readonly ProfilerMarker _pm_PreInvokeStopCallbacksEvent =
+ new("NetworkObject.PreInvokeStopCallbacks");
+ private static readonly ProfilerMarker _pm_PostInvokeStopCallbacksEvent =
+ new("NetworkObject.PostInvokeStopCallbacks");
+
+ #endregion
+
#endregion
// ReSharper disable Unity.PerformanceAnalysis
@@ -24,6 +161,9 @@ public partial class NetworkObject : MonoBehaviour
///
private void InvokeStartCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
+ using (_pm_PreInvokeStartCallbacksEvent.Auto())
+ PreInvokeStartCallbacks?.Invoke(this, asServer, invokeSyncTypeCallbacks);
+
/* Note: When invoking OnOwnership here previous owner will
* always be an empty connection, since the object is just
* now initializing. */
@@ -41,7 +181,7 @@ private void InvokeStartCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnStartServer_Internal();
- _onStartServerCalled = true;
+ OnStartServerCalled = true;
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnOwnershipServer_Internal(Managing.NetworkManager.EmptyConnection);
}
@@ -50,15 +190,21 @@ private void InvokeStartCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnStartClient_Internal();
- _onStartClientCalled = true;
+ OnStartClientCalled = true;
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnOwnershipClient_Internal(Managing.NetworkManager.EmptyConnection);
}
if (invokeSyncTypeCallbacks)
+ {
InvokeOnStartSyncTypeCallbacks(true);
+ OnStartSyncTypeCallbacksCalled = true;
+ }
InvokeStartCallbacks_Prediction(asServer);
+
+ using (_pm_PostInvokeStartCallbacksEvent.Auto())
+ PostInvokeStartCallbacks?.Invoke(this, asServer, invokeSyncTypeCallbacks);
}
///
@@ -109,22 +255,28 @@ internal void InvokeOnServerDespawn(NetworkConnection conn)
///
internal void InvokeStopCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
{
+ using (_pm_PreInvokeStopCallbacksEvent.Auto())
+ PreInvokeStopCallbacks?.Invoke(this, asServer, invokeSyncTypeCallbacks);
+
InvokeStopCallbacks_Prediction(asServer);
if (invokeSyncTypeCallbacks)
+ {
InvokeOnStopSyncTypeCallbacks(asServer);
+ OnStartSyncTypeCallbacksCalled = false;
+ }
- if (asServer && _onStartServerCalled)
+ if (asServer && OnStartServerCalled)
{
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnStopServer_Internal();
- if (!_onStartClientCalled)
+ if (!OnStartClientCalled)
InvokeOnNetwork();
- _onStartServerCalled = false;
+ OnStartServerCalled = false;
}
- else if (!asServer && _onStartClientCalled)
+ else if (!asServer && OnStartClientCalled)
{
for (int i = 0; i < NetworkBehaviours.Count; i++)
NetworkBehaviours[i].OnStopClient_Internal();
@@ -133,11 +285,14 @@ internal void InvokeStopCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
* that means this is still intialized on the server. This would
* happen if the object despawned for the clientHost but not on the
* server. */
- if (!_onStartServerCalled)
+ if (!OnStartServerCalled)
InvokeOnNetwork();
- _onStartClientCalled = false;
+ OnStartClientCalled = false;
}
+
+ using (_pm_PostInvokeStopCallbacksEvent.Auto())
+ PostInvokeStopCallbacks?.Invoke(this, asServer, invokeSyncTypeCallbacks);
void InvokeOnNetwork()
{
@@ -179,4 +334,4 @@ private void InvokeManualOwnershipChange(NetworkConnection prevOwner, bool asSer
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs
index ea647b28..e6749bc2 100644
--- a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs
@@ -68,7 +68,32 @@ internal void SetIsDestroying(DespawnType? despawnType = null)
/// True if this object has been initialized on the client side.
/// This is set true right before client start callbacks and after stop callbacks.
///
- public bool IsClientInitialized { get; private set; }
+ private bool _isClientInitialized;
+ ///
+ /// True if this object has been initialized on the client side.
+ /// This is set true right before client start callbacks and after stop callbacks.
+ ///
+ public bool IsClientInitialized
+ {
+ get => _isClientInitialized;
+ private set
+ {
+ if (_isClientInitialized == value)
+ return;
+
+ _isClientInitialized = value;
+ if (value)
+ {
+ using (_pm_OnClientInitializedEvent.Auto())
+ OnClientInitializedEvent?.Invoke(this);
+ }
+ else
+ {
+ using (_pm_OnClientDeinitializedEvent.Auto())
+ OnClientDeinitializedEvent?.Invoke(this);
+ }
+ }
+ }
///
/// True if the client is started and authenticated. This will return true on clientHost even if the object has not initialized yet for the client.
/// To check if this object has been initialized for the client use IsClientInitialized.
@@ -87,7 +112,32 @@ internal void SetIsDestroying(DespawnType? despawnType = null)
/// True if this object has been initialized on the server side.
/// This is set true right before server start callbacks and after stop callbacks.
///
- public bool IsServerInitialized { get; private set; }
+ private bool _isServerInitialized;
+ ///
+ /// True if this object has been initialized on the server side.
+ /// This is set true right before server start callbacks and after stop callbacks.
+ ///
+ public bool IsServerInitialized
+ {
+ get => _isServerInitialized;
+ private set
+ {
+ if (_isServerInitialized == value)
+ return;
+
+ _isServerInitialized = value;
+ if (value)
+ {
+ using (_pm_OnServerInitializedEvent.Auto())
+ OnServerInitializedEvent?.Invoke(this);
+ }
+ else
+ {
+ using (_pm_OnServerDeinitializedEvent.Auto())
+ OnServerDeinitializedEvent?.Invoke(this);
+ }
+ }
+ }
///
/// True if the server is active. This will return true on clientHost even if the object is being deinitialized on the server.
/// To check if this object has been initialized for the server use IsServerInitialized.
@@ -401,4 +451,4 @@ public void SetLocalOwnership(NetworkConnection caller, bool recursive)
public void UnregisterInstance() where T : UnityEngine.Component => NetworkManager.UnregisterInstance();
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
index 3be6a8db..a74c956c 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs
@@ -28,8 +28,7 @@ public class SyncBase
///
/// The settings for this SyncVar.
///
- [MakePublic]
- internal SyncTypeSettings Settings;
+ [MakePublic] public SyncTypeSettings Settings;
///
/// How often updates may send.
///
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
index 3dbdbc8b..564e6ef5 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs
@@ -446,6 +446,11 @@ protected internal override void ResetState(bool asServer)
foreach (KeyValuePair item in _initialValues)
Collection[item.Key] = item.Value;
}
+
+ if (asServer)
+ _serverOnChanges.Clear();
+ else
+ _clientOnChanges.Clear();
}
///
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
index aab05229..d6aee36c 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSet.cs
@@ -415,6 +415,11 @@ protected internal override void ResetState(bool asServer)
foreach (T item in _initialValues)
Collection.Add(item);
}
+
+ if (asServer)
+ _serverOnChanges.Clear();
+ else
+ _clientOnChanges.Clear();
}
///
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
index 9254ddad..510b785c 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs
@@ -460,6 +460,9 @@ protected internal override void ResetState(bool asServer)
foreach (T item in _initialValues)
Collection.Add(item);
}
+
+ if (asServer) _serverOnChanges.Clear();
+ else _clientOnChanges.Clear();
}
///
diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
index 06a86a33..864fdcdd 100644
--- a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
+++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs
@@ -10,7 +10,7 @@
namespace FishNet.Object.Synchronizing
{
- internal interface ISyncVar { }
+ public interface ISyncVar { }
[APIExclude]
[System.Serializable]
@@ -468,6 +468,9 @@ protected internal override void ResetState(bool asServer)
_value = _initialValue;
_valueSetAfterInitialized = false;
}
+
+ if (asServer) _serverOnChange = null;
+ else _clientOnChange = null;
}
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef
index 438b416f..751eaca6 100644
--- a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef
@@ -2,7 +2,10 @@
"name": "GameKit.Dependencies",
"rootNamespace": "",
"references": [
- "GUID:6055be8ebefd69e48b49212b09b47b2f"
+ "GUID:6055be8ebefd69e48b49212b09b47b2f",
+ "GUID:d8b63aba1907145bea998dd612889d6b",
+ "GUID:2665a8d13d1b3f18800f46e256720795",
+ "GUID:e0cd26848372d4e5c891c569017e11f1"
],
"includePlatforms": [],
"excludePlatforms": [],
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs
index b6a56652..d7d65070 100644
--- a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs
@@ -8,7 +8,7 @@ public static class DictionaryFN
/// Uses a hacky way to TryGetValue on a dictionary when using IL2CPP and on mobile.
/// This is to support older devices that don't properly handle IL2CPP builds.
///
- public static bool TryGetValueIL2CPP(this IDictionary dict, TKey key, out TValue value)
+ public static bool TryGetValueIL2CPP(this IReadOnlyDictionary dict, TKey key, out TValue value)
{
#if ENABLE_IL2CPP && UNITY_IOS || UNITY_ANDROID
if (dict.ContainsKey(key))
@@ -30,7 +30,7 @@ public static bool TryGetValueIL2CPP(this IDictionary
///
- public static List ValuesToList(this IDictionary dict, bool useCache)
+ public static List ValuesToList(this IReadOnlyDictionary dict, bool useCache)
{
List result = useCache ? CollectionCaches.RetrieveList() : new(dict.Count);
@@ -43,7 +43,7 @@ public static List ValuesToList(this IDictionary
/// Adds values to a list.
///
- public static void ValuesToList(this IDictionary dict, ref List result, bool clearLst)
+ public static void ValuesToList(this IReadOnlyDictionary dict, ref List result, bool clearLst)
{
if (clearLst)
result.Clear();
@@ -55,7 +55,7 @@ public static void ValuesToList(this IDictionary dic
///
/// Returns keys as a list.
///
- public static List KeysToList(this IDictionary dict, bool useCache)
+ public static List KeysToList(this IReadOnlyDictionary dict, bool useCache)
{
List result = useCache ? CollectionCaches.RetrieveList() : new(dict.Count);
@@ -68,7 +68,7 @@ public static List KeysToList(this IDictionary
///
/// Adds keys to a list.
///
- public static void KeysToList(this IDictionary dict, ref List result, bool clearLst)
+ public static void KeysToList(this IReadOnlyDictionary dict, ref List result, bool clearLst)
{
result.Clear();
diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Guids.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Guids.cs
new file mode 100644
index 00000000..cb7d3635
--- /dev/null
+++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Guids.cs
@@ -0,0 +1,10 @@
+namespace GameKit.Dependencies.Utilities
+{
+ public static class Guids
+ {
+ ///
+ /// A buffer convert data and discard.
+ ///
+ public static byte[] Buffer = new byte[16];
+ }
+}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Serializing/Reader.cs b/Assets/FishNet/Runtime/Serializing/Reader.cs
index 935b1122..aa24f747 100644
--- a/Assets/FishNet/Runtime/Serializing/Reader.cs
+++ b/Assets/FishNet/Runtime/Serializing/Reader.cs
@@ -97,10 +97,6 @@ public enum DataSource
/// Used to convert bytes to a string.
///
private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
- ///
- /// Used to convert bytes to a GUID.
- ///
- private static readonly byte[] _guidBuffer = new byte[16];
#endregion
public Reader() { }
@@ -899,7 +895,7 @@ public byte[] ReadUInt8ArrayAllocated(int count)
[DefaultReader]
public Guid ReadGuid()
{
- byte[] buffer = _guidBuffer;
+ byte[] buffer = Guids.Buffer;
ReadUInt8Array(ref buffer, 16);
return new(buffer);
}
diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs b/Assets/FishNet/Runtime/Serializing/Writer.cs
index 39ce4904..5a0a4ced 100644
--- a/Assets/FishNet/Runtime/Serializing/Writer.cs
+++ b/Assets/FishNet/Runtime/Serializing/Writer.cs
@@ -857,6 +857,17 @@ public void WriteMatrix4x4Unpacked(Matrix4x4 value)
///
///
[DefaultWriter]
+ public void WriteGuid(Guid value)
+ {
+ byte[] data = Guids.Buffer;
+ value.TryWriteBytes(data);
+ WriteUInt8Array(data, 0, data.Length);
+ }
+
+ ///
+ /// Writes a Guid.
+ ///
+ ///
public void WriteGuidAllocated(Guid value)
{
byte[] data = value.ToByteArray();
diff --git a/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs
index fcd41774..b1ecaeab 100644
--- a/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs
+++ b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs
@@ -75,7 +75,7 @@
// // public static void WriteRay(this Writer writer, Ray value) => writer.WriteRay(value);
// // public static void WriteRay2D(this Writer writer, Ray2D value) => writer.WriteRay2D(value);
// // public static void WriteMatrix4x4(this Writer writer, Matrix4x4 value) => writer.WriteMatrix4x4(value);
-// // public static void WriteGuidAllocated(this Writer writer, System.Guid value) => writer.WriteGuidAllocated(value);
+// // public static void WriteGuid(this Writer writer, System.Guid value) => writer.WriteGuid(value);
// // public static void WriteGameObject(this Writer writer, GameObject value) => writer.WriteGameObject(value);
// // public static void WriteTransform(this Writer writer, Transform value) => writer.WriteTransform(value);
// // public static void WriteNetworkObject(this Writer writer, NetworkObject value) => writer.WriteNetworkObject(value);
diff --git a/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs
index 301b71e2..b92f9bd9 100644
--- a/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs
+++ b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs
@@ -3,6 +3,7 @@
using FishNet.Object;
using System.Runtime.CompilerServices;
using UnityEngine;
+using UnityEngine.Jobs;
namespace FishNet.Utility.Extension
{
@@ -10,48 +11,124 @@ namespace FishNet.Utility.Extension
public static class TransformFN
{
///
- /// Sets values of TransformProperties to a transforms world properties.
+ /// Gets correct values of Vector3 pos and Quaternion rot
+ ///
+ public static void GetCorrectLocalPositionAndRotation(this TransformAccess t, out Vector3 pos, out Quaternion rot)
+ {
+ // https://issuetracker.unity3d.com/issues/wrong-position-and-rotation-values-are-returned-when-using-transformaccess-dot-getlocalpositionandrotation
+ pos = t.localPosition;
+ rot = t.localRotation;
+ }
+
+ ///
+ /// Sets correct values of Vector3 pos and Quaternion rot
+ ///
+ public static void SetCorrectLocalPositionAndRotation(this TransformAccess t, Vector3 pos, Quaternion rot)
+ {
+ // https://issuetracker.unity3d.com/issues/wrong-position-and-rotation-values-are-returned-when-using-transformaccess-dot-getlocalpositionandrotation
+ t.localPosition = pos;
+ t.localRotation = rot;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
///
public static TransformProperties GetWorldProperties(this Transform t)
{
- TransformProperties tp = new(t.position, t.rotation, t.localScale);
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
+ return tp;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
+ ///
+ public static TransformProperties GetWorldProperties(this TransformAccess t)
+ {
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
return tp;
}
///
- /// Sets values of TransformProperties to a transforms world properties.
+ /// Gets values of TransformProperties from the transforms world properties.
///
public static TransformProperties GetWorldProperties(this Transform t, TransformProperties offset)
{
- TransformProperties tp = new(t.position, t.rotation, t.localScale);
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
+ tp.Add(offset);
+ return tp;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
+ ///
+ public static TransformProperties GetWorldProperties(this TransformAccess t, TransformProperties offset)
+ {
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
tp.Add(offset);
return tp;
}
///
- /// Sets values of TransformProperties to a transforms world properties.
+ /// Gets values of TransformProperties from the transforms world properties.
///
public static TransformPropertiesCls GetWorldPropertiesCls(this Transform t)
{
- TransformPropertiesCls tp = new(t.position, t.rotation, t.localScale);
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformPropertiesCls tp = new(pos, rot, t.localScale);
+ return tp;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
+ ///
+ public static TransformPropertiesCls GetWorldPropertiesCls(this TransformAccess t)
+ {
+ t.GetPositionAndRotation(out var pos, out var rot);
+ TransformPropertiesCls tp = new(pos, rot, t.localScale);
return tp;
}
///
- /// Sets values of TransformProperties to a transforms world properties.
+ /// Gets values of TransformProperties from the transforms world properties.
///
public static TransformProperties GetLocalProperties(this Transform t)
{
- TransformProperties tp = new(t.localPosition, t.localRotation, t.localScale);
+ t.GetLocalPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
+ return tp;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
+ ///
+ public static TransformProperties GetLocalProperties(this TransformAccess t)
+ {
+ t.GetCorrectLocalPositionAndRotation(out var pos, out var rot);
+ TransformProperties tp = new(pos, rot, t.localScale);
return tp;
}
///
- /// Sets values of TransformProperties to a transforms world properties.
+ /// Gets values of TransformProperties from the transforms world properties.
///
public static TransformPropertiesCls GetLocalPropertiesCls(this Transform t)
{
- TransformPropertiesCls tp = new(t.localPosition, t.localRotation, t.localScale);
+ t.GetLocalPositionAndRotation(out var pos, out var rot);
+ TransformPropertiesCls tp = new(pos, rot, t.localScale);
+ return tp;
+ }
+
+ ///
+ /// Gets values of TransformProperties from the transforms world properties.
+ ///
+ public static TransformPropertiesCls GetLocalPropertiesCls(this TransformAccess t)
+ {
+ t.GetCorrectLocalPositionAndRotation(out var pos, out var rot);
+ TransformPropertiesCls tp = new(pos, rot, t.localScale);
return tp;
}
@@ -70,8 +147,10 @@ public static void SetTransformOffsets(this Transform t, Transform target, ref V
{
if (target == null)
return;
- pos = target.position - t.position;
- rot = target.rotation * Quaternion.Inverse(t.rotation);
+ t.GetPositionAndRotation(out var tPos, out var tRot);
+ target.GetPositionAndRotation(out var targetPos, out var targetRot);
+ pos = targetPos - tPos;
+ rot = targetRot * Quaternion.Inverse(tRot);
}
///
@@ -83,7 +162,9 @@ public static TransformProperties GetTransformOffsets(this Transform t, Transfor
if (target == null)
return default;
- return new(target.position - t.position, target.rotation * Quaternion.Inverse(t.rotation), target.localScale - t.localScale);
+ t.GetPositionAndRotation(out var tPos, out var tRot);
+ target.GetPositionAndRotation(out var targetPos, out var targetRot);
+ return new(targetPos - tPos, targetRot * Quaternion.Inverse(tRot), target.localScale - t.localScale);
}
///
@@ -91,8 +172,16 @@ public static TransformProperties GetTransformOffsets(this Transform t, Transfor
///
public static void SetLocalProperties(this Transform t, TransformPropertiesCls tp)
{
- t.localPosition = tp.Position;
- t.localRotation = tp.Rotation;
+ t.SetLocalPositionAndRotation(tp.Position, tp.Rotation);
+ t.localScale = tp.LocalScale;
+ }
+
+ ///
+ /// Sets a transform to local properties.
+ ///
+ public static void SetLocalProperties(this TransformAccess t, TransformPropertiesCls tp)
+ {
+ t.SetCorrectLocalPositionAndRotation(tp.Position, tp.Rotation);
t.localScale = tp.LocalScale;
}
@@ -101,8 +190,16 @@ public static void SetLocalProperties(this Transform t, TransformPropertiesCls t
///
public static void SetLocalProperties(this Transform t, TransformProperties tp)
{
- t.localPosition = tp.Position;
- t.localRotation = tp.Rotation;
+ t.SetLocalPositionAndRotation(tp.Position, tp.Rotation);
+ t.localScale = tp.Scale;
+ }
+
+ ///
+ /// Sets a transform to local properties.
+ ///
+ public static void SetLocalProperties(this TransformAccess t, TransformProperties tp)
+ {
+ t.SetCorrectLocalPositionAndRotation(tp.Position, tp.Rotation);
t.localScale = tp.Scale;
}
@@ -111,8 +208,16 @@ public static void SetLocalProperties(this Transform t, TransformProperties tp)
///
public static void SetWorldProperties(this Transform t, TransformPropertiesCls tp)
{
- t.position = tp.Position;
- t.rotation = tp.Rotation;
+ t.SetPositionAndRotation(tp.Position, tp.Rotation);
+ t.localScale = tp.LocalScale;
+ }
+
+ ///
+ /// Sets a transform to world properties.
+ ///
+ public static void SetWorldProperties(this TransformAccess t, TransformPropertiesCls tp)
+ {
+ t.SetPositionAndRotation(tp.Position, tp.Rotation);
t.localScale = tp.LocalScale;
}
@@ -121,8 +226,16 @@ public static void SetWorldProperties(this Transform t, TransformPropertiesCls t
///
public static void SetWorldProperties(this Transform t, TransformProperties tp)
{
- t.position = tp.Position;
- t.rotation = tp.Rotation;
+ t.SetPositionAndRotation(tp.Position, tp.Rotation);
+ t.localScale = tp.Scale;
+ }
+
+ ///
+ /// Sets a transform to world properties.
+ ///
+ public static void SetWorldProperties(this TransformAccess t, TransformProperties tp)
+ {
+ t.SetPositionAndRotation(tp.Position, tp.Rotation);
t.localScale = tp.Scale;
}
@@ -131,8 +244,15 @@ public static void SetWorldProperties(this Transform t, TransformProperties tp)
///
public static void SetLocalPositionAndRotation(this Transform t, Vector3 pos, Quaternion rot)
{
- t.localPosition = pos;
- t.localRotation = rot;
+ t.SetLocalPositionAndRotation(pos, rot);
+ }
+
+ ///
+ /// Sets local position and rotation for a transform.
+ ///
+ public static void SetLocalPositionAndRotation(this TransformAccess t, Vector3 pos, Quaternion rot)
+ {
+ t.SetCorrectLocalPositionAndRotation(pos, rot);
}
///
@@ -140,8 +260,16 @@ public static void SetLocalPositionAndRotation(this Transform t, Vector3 pos, Qu
///
public static void SetLocalPositionRotationAndScale(this Transform t, Vector3 pos, Quaternion rot, Vector3 scale)
{
- t.localPosition = pos;
- t.localRotation = rot;
+ t.SetLocalPositionAndRotation(pos, rot);
+ t.localScale = scale;
+ }
+
+ ///
+ /// Sets local position, rotation, and scale for a transform.
+ ///
+ public static void SetLocalPositionRotationAndScale(this TransformAccess t, Vector3 pos, Quaternion rot, Vector3 scale)
+ {
+ t.SetCorrectLocalPositionAndRotation(pos, rot);
t.localScale = scale;
}
@@ -151,8 +279,29 @@ public static void SetLocalPositionRotationAndScale(this Transform t, Vector3 po
public static void SetLocalPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
{
if (nullablePos.HasValue)
- t.localPosition = nullablePos.Value;
- if (nullableRot.HasValue)
+ {
+ if (nullableRot.HasValue)
+ t.SetLocalPositionAndRotation(nullablePos.Value, nullableRot.Value);
+ else t.localPosition = nullablePos.Value;
+ }
+ else if (nullableRot.HasValue)
+ t.localRotation = nullableRot.Value;
+ if (nullableScale.HasValue)
+ t.localScale = nullableScale.Value;
+ }
+
+ ///
+ /// Sets local position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped.
+ ///
+ public static void SetLocalPositionRotationAndScale(this TransformAccess t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
+ {
+ if (nullablePos.HasValue)
+ {
+ if (nullableRot.HasValue)
+ t.SetCorrectLocalPositionAndRotation(nullablePos.Value, nullableRot.Value);
+ else t.localPosition = nullablePos.Value;
+ }
+ else if (nullableRot.HasValue)
t.localRotation = nullableRot.Value;
if (nullableScale.HasValue)
t.localScale = nullableScale.Value;
@@ -164,8 +313,29 @@ public static void SetLocalPositionRotationAndScale(this Transform t, Vector3? n
public static void SetWorldPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
{
if (nullablePos.HasValue)
- t.position = nullablePos.Value;
- if (nullableRot.HasValue)
+ {
+ if (nullableRot.HasValue)
+ t.SetPositionAndRotation(nullablePos.Value, nullableRot.Value);
+ else t.position = nullablePos.Value;
+ }
+ else if (nullableRot.HasValue)
+ t.rotation = nullableRot.Value;
+ if (nullableScale.HasValue)
+ t.localScale = nullableScale.Value;
+ }
+
+ ///
+ /// Sets world position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped.
+ ///
+ public static void SetWorldPositionRotationAndScale(this TransformAccess t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
+ {
+ if (nullablePos.HasValue)
+ {
+ if (nullableRot.HasValue)
+ t.SetPositionAndRotation(nullablePos.Value, nullableRot.Value);
+ else t.position = nullablePos.Value;
+ }
+ else if (nullableRot.HasValue)
t.rotation = nullableRot.Value;
if (nullableScale.HasValue)
t.localScale = nullableScale.Value;
@@ -176,8 +346,56 @@ public static void SetWorldPositionRotationAndScale(this Transform t, Vector3? n
///
public static void OutLocalPropertyValues(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale)
{
- pos = nullablePos == null ? t.localPosition : nullablePos.Value;
- rot = nullableRot == null ? t.localRotation : nullableRot.Value;
+ if (!nullablePos.HasValue)
+ {
+ if (!nullableRot.HasValue)
+ t.GetLocalPositionAndRotation(out pos, out rot);
+ else
+ {
+ pos = t.localPosition;
+ rot = nullableRot.Value;
+ }
+ }
+ else if (!nullableRot.HasValue)
+ {
+ pos = nullablePos.Value;
+ rot = t.localRotation;
+ }
+ else
+ {
+ pos = nullablePos.Value;
+ rot = nullableRot.Value;
+ }
+
+ scale = nullableScale == null ? t.localScale : nullableScale.Value;
+ }
+
+ ///
+ /// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used.
+ ///
+ public static void OutLocalPropertyValues(this TransformAccess t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale)
+ {
+ if (!nullablePos.HasValue)
+ {
+ if (!nullableRot.HasValue)
+ t.GetCorrectLocalPositionAndRotation(out pos, out rot);
+ else
+ {
+ pos = t.localPosition;
+ rot = nullableRot.Value;
+ }
+ }
+ else if (!nullableRot.HasValue)
+ {
+ pos = nullablePos.Value;
+ rot = t.localRotation;
+ }
+ else
+ {
+ pos = nullablePos.Value;
+ rot = nullableRot.Value;
+ }
+
scale = nullableScale == null ? t.localScale : nullableScale.Value;
}
@@ -186,8 +404,56 @@ public static void OutLocalPropertyValues(this Transform t, Vector3? nullablePos
///
public static void OutWorldPropertyValues(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale)
{
- pos = nullablePos == null ? t.position : nullablePos.Value;
- rot = nullableRot == null ? t.rotation : nullableRot.Value;
+ if (!nullablePos.HasValue)
+ {
+ if (!nullableRot.HasValue)
+ t.GetPositionAndRotation(out pos, out rot);
+ else
+ {
+ pos = t.position;
+ rot = nullableRot.Value;
+ }
+ }
+ else if (!nullableRot.HasValue)
+ {
+ pos = nullablePos.Value;
+ rot = t.rotation;
+ }
+ else
+ {
+ pos = nullablePos.Value;
+ rot = nullableRot.Value;
+ }
+
+ scale = nullableScale == null ? t.localScale : nullableScale.Value;
+ }
+
+ ///
+ /// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used.
+ ///
+ public static void OutWorldPropertyValues(this TransformAccess t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale)
+ {
+ if (!nullablePos.HasValue)
+ {
+ if (!nullableRot.HasValue)
+ t.GetPositionAndRotation(out pos, out rot);
+ else
+ {
+ pos = t.position;
+ rot = nullableRot.Value;
+ }
+ }
+ else if (!nullableRot.HasValue)
+ {
+ pos = nullablePos.Value;
+ rot = t.rotation;
+ }
+ else
+ {
+ pos = nullablePos.Value;
+ rot = nullableRot.Value;
+ }
+
scale = nullableScale == null ? t.localScale : nullableScale.Value;
}
}