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 9b38e82d..7dbb8bc2 100644
--- a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
@@ -120,7 +120,6 @@ public void SetRemoteServerTimeout(RemoteTimeoutType timeoutType, ushort duratio
[SerializeField]
private ushort _frameRate = NetworkManager.MAXIMUM_FRAMERATE;
- ///
/// Sets the maximum frame rate the client may run at. Calling this method will enable ChangeFrameRate.
///
/// New value.
@@ -144,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()
@@ -310,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);
+ }
}
///
@@ -350,7 +380,10 @@ private void Transport_OnClientConnectionState(ClientConnectionStateArgs args)
///
private void Transport_OnClientReceivedData(ClientReceivedDataArgs args)
{
- ParseReceived(args);
+ using (_pm_Transport_OnClientReceivedData.Auto())
+ {
+ ParseReceived(args);
+ }
}
///
@@ -358,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();
+ }
}
///
@@ -374,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)
- {
- 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)
+ using (_pm_ParseReader_ReadPacketId.Auto())
{
- 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
}
///
@@ -717,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 d9527bab..78f208c1 100644
--- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
@@ -172,7 +172,6 @@ public void SetRemoteClientTimeout(RemoteTimeoutType timeoutType, ushort duratio
[SerializeField]
private ushort _frameRate = NetworkManager.MAXIMUM_FRAMERATE;
- ///
/// Sets the maximum frame rate the client may run at. Calling this method will enable ChangeFrameRate.
///
/// New value.
@@ -223,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.
@@ -234,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.
@@ -523,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);
+ }
}
///
@@ -609,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)
- {
- 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
+ using (_pm_Transport_OnRemoteConnectionState.Auto())
{
- //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}.");
+ }
}
}
}
@@ -700,7 +711,10 @@ private void SendAuthenticated(NetworkConnection conn)
///
private void Transport_OnServerReceivedData(ServerReceivedDataArgs args)
{
- ParseReceived(args);
+ using (_pm_Transport_OnServerReceivedData.Auto())
+ {
+ ParseReceived(args);
+ }
}
///
@@ -990,4 +1004,4 @@ private void BroadcastClientConnectionChange(bool connected, NetworkConnection c
}
}
}
-}
\ No newline at end of file
+}
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;
}
}