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 5e8daa71..46338147 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
@@ -371,13 +385,13 @@ public void InitializeState() { }
Rotation = AutoPackType.Packed,
Scale = AutoPackType.Unpacked
};
- ///
/// True to use scaled deltaTime when smoothing.
///
[Tooltip("True to use scaled deltaTime when smoothing.")]
[SerializeField]
private bool _useScaledTime = true;
///
+ ///
/// How many ticks to interpolate.
///
[Tooltip("How many ticks to interpolate.")]
@@ -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.
///
@@ -646,19 +677,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.
///
@@ -779,7 +797,6 @@ private void TryClearGoalDatas_OwnershipChange(NetworkConnection prevOwner, bool
* follow the queue. */
}
-
private void TimeManager_OnUpdate()
{
using (_pm_OnUpdate.Auto())
@@ -1026,6 +1043,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.
@@ -1044,6 +1062,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())
@@ -1063,9 +1082,12 @@ 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)
{
+#if UNITY_EDITOR || DEVELOPMENT_BUILD
using (_pm_ForceSend1.Auto())
+#endif
{
/* If there is a pending delayed force send then queue it
* immediately and set a new delay tick. */
@@ -1078,6 +1100,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())
@@ -1109,6 +1132,7 @@ public void SetInterval(byte value)
/// Updates the interval value.
///
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetIntervalInternal(byte value)
{
value = (byte)Mathf.Max(value, 1);
@@ -1165,17 +1189,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);
}
}
@@ -1192,220 +1218,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;
-
- 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);
- }
- }
-
- //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);
- }
- }
+ bool canUpdateData = dataToUpdate != null;
+ if (canUpdateData && changed != ChangedDelta.Unset)
+ dataToUpdate.SetIsDefaultToFalse();
- /* If there is a teleport pending then apply
- * extended flag since thats where teleport resides. */
- bool teleport = _teleport;
- if (teleport)
- changed |= ChangedDelta.Extended;
+ 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;
- 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;
+
+ if (canUpdateData)
+ dataToUpdate.Scale.y = original;
- //Insert flags.
- writer.InsertUInt8Unpacked((byte)flagsA, startIndexA);
+ 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);
+ }
- bool ChangedContains(ChangedDelta whole, ChangedDelta part)
+ //Insert flags.
+ writer.InsertUInt8Unpacked((byte)flagsA, startIndexA);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool ChangedContains(ChangedDelta whole, ChangedDelta part)
{
return (whole & part) == part;
}
@@ -1501,29 +1533,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;
}
@@ -1614,118 +1649,124 @@ private void MoveToTarget(float delta)
{
using (_pm_MoveToTarget.Auto())
{
- if (_currentGoalData == null)
- return;
-
- //Cannot move if neither is active.
- if (!IsServerInitialized && !IsClientInitialized)
- return;
-
- //If client auth and the owner don't move towards target.
- if (_clientAuthoritative)
- {
- if (IsOwner || TakenOwnership)
- return;
- }
- else
- {
- //If not client authoritative, is owner, and don't sync to owner.
- if (IsOwner && !_sendToOwner)
+ if (_currentGoalData == null)
return;
- }
-
- //True if not client controlled.
- bool controlledByClient = _clientAuthoritative && Owner.IsActive;
- //If not controlled by client and is server then no reason to move.
- if (!controlledByClient && IsServerInitialized)
- return;
- /* Once here it's safe to assume the object will be moving.
- * Any checks which would stop it from moving be it client
- * auth and owner, or server controlled and server, ect,
- * would have already been run. */
- TransformData td = _currentGoalData.Transforms;
- RateData rd = _currentGoalData.Rates;
+ //Cannot move if neither is active.
+ if (!IsServerInitialized && !IsClientInitialized)
+ return;
- //Set parent.
- if (_synchronizeParent)
- SetParent(td.ParentBehaviour, rd);
+ //If client auth and the owner don't move towards target.
+ if (_clientAuthoritative)
+ {
+ if (IsOwner || TakenOwnership)
+ return;
+ }
+ else
+ {
+ //If not client authoritative, is owner, and don't sync to owner.
+ if (IsOwner && !_sendToOwner)
+ return;
+ }
- float multiplier = 1f;
- int queueCount = _goalDataQueue.Count;
- //Increase move rate slightly if over queue count.
- if (queueCount > _interpolation + 1)
- multiplier += 0.05f;
+ //True if not client controlled.
+ bool controlledByClient = _clientAuthoritative && Owner.IsActive;
+ //If not controlled by client and is server then no reason to move.
+ if (!controlledByClient && IsServerInitialized)
+ return;
- //Rate to update. Changes per property.
- float rate;
- Transform t = _cachedTransform;
+ /* Once here it's safe to assume the object will be moving.
+ * Any checks which would stop it from moving be it client
+ * auth and owner, or server controlled and server, ect,
+ * would have already been run. */
+ TransformData td = _currentGoalData.Transforms;
+ RateData rd = _currentGoalData.Rates;
+
+ //Set parent.
+ if (_synchronizeParent)
+ SetParent(td.ParentBehaviour, rd);
+
+ float multiplier = 1f;
+ int queueCount = _goalDataQueue.Count;
+ //Increase move rate slightly if over queue count.
+ if (queueCount > _interpolation + 1)
+ multiplier += 0.05f;
+
+ //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);
+ //Snap any bits of the transform that should be.
+ SnapProperties(td);
- //Position.
- if (_synchronizePosition)
- {
- rate = rd.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);
- }
+ //Position.
+ if (_synchronizePosition)
+ {
+ rate = rd.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(localPosition, posGoal, rate * delta * multiplier);
+ }
- //Rotation.
- if (_synchronizeRotation)
- {
- rate = rd.Rotation;
- // ReSharper disable once CompareOfFloatsByEqualityOperator
- if (rate == -1f)
- t.localRotation = td.Rotation;
- else
- t.localRotation = Quaternion.RotateTowards(t.localRotation, td.Rotation, rate * delta);
- }
+ //Rotation.
+ if (_synchronizeRotation)
+ {
+ rate = rd.Rotation;
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
+ if (rate == -1f)
+ t.localRotation = td.Rotation;
+ else
+ t.localRotation = Quaternion.RotateTowards(localRotation, td.Rotation, rate * delta);
+ }
- //Scale.
- if (_synchronizeScale)
- {
- rate = rd.Scale;
- // ReSharper disable once CompareOfFloatsByEqualityOperator
- if (rate == -1f)
- t.localScale = td.Scale;
- else
- t.localScale = Vector3.MoveTowards(t.localScale, td.Scale, rate * delta);
- }
+ //Scale.
+ if (_synchronizeScale)
+ {
+ rate = rd.Scale;
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
+ if (rate == -1f)
+ t.localScale = td.Scale;
+ else
+ t.localScale = Vector3.MoveTowards(localScale, td.Scale, rate * delta);
+ }
- float timeRemaining = rd.TimeRemaining - delta * multiplier;
- if (timeRemaining < -delta)
- timeRemaining = -delta;
- rd.TimeRemaining = timeRemaining;
+ float timeRemaining = rd.TimeRemaining - delta * multiplier;
+ if (timeRemaining < -delta)
+ timeRemaining = -delta;
+ rd.TimeRemaining = timeRemaining;
- if (rd.TimeRemaining <= 0f)
- {
- float leftOver = Mathf.Abs(rd.TimeRemaining);
- //If more in buffer then run next buffer.
- if (queueCount > 0)
+ if (rd.TimeRemaining <= 0f)
{
- SetCurrentGoalData(_goalDataQueue.Dequeue());
- if (leftOver > 0f)
- MoveToTarget(leftOver);
+ float leftOver = Mathf.Abs(rd.TimeRemaining);
+ //If more in buffer then run next buffer.
+ if (queueCount > 0)
+ {
+ SetCurrentGoalData(_goalDataQueue.Dequeue());
+ if (leftOver > 0f)
+ MoveToTarget(leftOver);
+ }
+ //No more in buffer, see if can extrapolate.
+ else
+ {
+ /* 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
+ * and it's thrown off. */
+ if (!HasChanged(td))
+ _currentGoalData = null;
+ OnInterpolationComplete?.Invoke();
+
+ }
}
- //No more in buffer, see if can extrapolate.
- else
- {
- /* 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
- * and it's thrown off. */
- if (!HasChanged(td))
- _currentGoalData = null;
- OnInterpolationComplete?.Invoke();
- }
- }
}
}
@@ -1748,7 +1789,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. */
@@ -1765,7 +1807,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;
}
@@ -1804,7 +1847,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);
@@ -1861,7 +1905,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.
@@ -1878,10 +1923,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;
}
@@ -1889,6 +1936,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;
@@ -1926,6 +1974,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.
@@ -1945,39 +1994,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;
-
- if (changed != ChangedDelta.Unset && ParentBehaviour != null)
- changed |= ChangedDelta.Nested;
-
- //If added scale or childed then also add extended.
- if (startChanged != changed)
- changed |= ChangedDelta.Extended;
-
- return 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;
+
+ if (changed != ChangedDelta.Unset && ParentBehaviour != null)
+ changed |= ChangedDelta.Nested;
+
+ //If added scale or childed then also add extended.
+ if (startChanged != changed)
+ changed |= ChangedDelta.Extended;
+
+ return changed;
+ }
}
#endregion
@@ -1993,36 +2044,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;
}
}
@@ -2030,6 +2083,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.
@@ -2192,7 +2246,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;
}
@@ -2201,7 +2256,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;
@@ -2213,6 +2269,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;
@@ -2236,12 +2293,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.
@@ -2308,108 +2365,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)
+ {
+ 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)
{
- GoalData tmpGd = _goalDataQueue.Dequeue();
- ResettableObjectCaches.Store(tmpGd);
+ if (_goalDataQueue.Count > 0)
+ {
+ SetCurrentGoalData(_goalDataQueue.Dequeue());
+ /* If is reliable and has changed then also
+ * enqueue latest. */
+ if (hasChanged)
+ _goalDataQueue.Enqueue(nextGd);
+ }
+ else
+ {
+ SetCurrentGoalData(nextGd);
+ }
+ }
+ /* If here then there's not enough in buffer to begin
+ * so add onto the buffer. */
+ else
+ {
+ _goalDataQueue.Enqueue(nextGd);
}
- //Snap to the next data to fix any smoothing timings.
- SetCurrentGoalData(_goalDataQueue.Dequeue());
- SetInstantRates(_currentGoalData!.Rates, 1, -1f);
- SnapProperties(_currentGoalData.Transforms, true);
+ /* If the queue is excessive beyond interpolation then
+ * dequeue extras to prevent from dropping behind too
+ * quickly. This shouldn't be an issue with normal movement
+ * as the NT speeds up if the buffer unexpectedly grows, but
+ * when connections are unstable results may come in chunks
+ * and for a better experience the older parts of the chunks
+ * will be dropped. */
+ if (_goalDataQueue.Count > _interpolation + 3)
+ {
+ while (_goalDataQueue.Count > _interpolation)
+ {
+ GoalData tmpGd = _goalDataQueue.Dequeue();
+ ResettableObjectCaches.Store(tmpGd);
+ }
+
+ //Snap to the next data to fix any smoothing timings.
+ SetCurrentGoalData(_goalDataQueue.Dequeue());
+ SetInstantRates(_currentGoalData!.Rates, 1, -1f);
+ SnapProperties(_currentGoalData.Transforms, true);
+ }
}
}
diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
index d3e6ca6d..6e717c86 100644
--- a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs
@@ -143,7 +143,7 @@ public void SetFrameRate(ushort value)
private SplitReader _splitReader = new();
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#endregion
#region Private Profiler Markers
diff --git a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
index 461c5418..9435fbca 100644
--- a/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
+++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs
@@ -99,7 +99,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();
}
}
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..2051ee58 100644
--- a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
+++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs
@@ -23,10 +23,20 @@ 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 Protected.
@@ -55,7 +65,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 +138,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 +345,7 @@ internal virtual void DespawnWithoutSynchronization(bool recursive, bool asServe
DespawnWithoutSynchronization(nob, recursive, asServer, nob.GetDefaultDespawnType(), removeFromSpawned: false);
}
- Spawned.Clear();
+ HandleClear();
}
///
@@ -371,7 +400,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 +437,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;
@@ -512,4 +541,5 @@ protected void CheckReadSceneObjectDetails(Reader r, ref string sceneName, ref s
}
#endif
}
-}
\ No newline at end of file
+
+}
diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
index 64ad0aa0..eeef7a1a 100644
--- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs
@@ -178,14 +178,15 @@ public void Despawn(NetworkObject networkObject, DespawnType? despawnType = null
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
if (!conn.IsValid)
return;
OnClientKick?.Invoke(conn, conn.ClientId, kickReason);
if (conn.IsActive)
- conn.Disconnect(true);
+ conn.Disconnect(immediately);
if (!string.IsNullOrEmpty(log))
NetworkManager.Log(loggingType, log);
@@ -198,10 +199,11 @@ public void Kick(NetworkConnection conn, KickReason kickReason, LoggingType logg
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(int clientId, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
OnClientKick?.Invoke(null, clientId, kickReason);
- NetworkManager.TransportManager.Transport.StopConnection(clientId, true);
+ NetworkManager.TransportManager.Transport.StopConnection(clientId, immediately);
if (!string.IsNullOrEmpty(log))
NetworkManager.Log(loggingType, log);
}
@@ -214,10 +216,11 @@ public void Kick(int clientId, KickReason kickReason, LoggingType loggingType =
/// Reason client is being kicked.
/// How to print logging as.
/// Optional message to be debug logged.
- public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
+ ///
+ public void Kick(NetworkConnection conn, Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "", bool immediately = true)
{
reader.Clear();
- Kick(conn, kickReason, loggingType, log);
+ Kick(conn, kickReason, loggingType, log, immediately);
}
}
}
\ No newline at end of file
diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
index 55d879d0..1365a5b8 100644
--- a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs
@@ -222,7 +222,7 @@ public void SetFrameRate(ushort value)
private SplitReader _splitReader = new();
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#if DEVELOPMENT
///
/// Logs data about parser to help debug.
diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
index c81ec7d1..58c37480 100644
--- a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs
@@ -8,6 +8,7 @@
using System;
using System.Runtime.CompilerServices;
using FishNet.Managing.Statistic;
+using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine;
using SystemStopwatch = System.Diagnostics.Stopwatch;
@@ -284,10 +285,11 @@ 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_OnFixedUpdate = new("TimeManager.OnFixedUpdate()");
private static readonly ProfilerMarker _pm_OnPostPhysicsSimulation = new("TimeManager.OnPostPhysicsSimulation(float)");
private static readonly ProfilerMarker _pm_OnPrePhysicsSimulation = new("TimeManager.OnPrePhysicsSimulation(float)");
@@ -683,97 +685,100 @@ 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;
- }
-
- double time = Time.unscaledDeltaTime;
+ bool isClient = NetworkManager.IsClientStarted;
+ bool isServer = NetworkManager.IsServerStarted;
- _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)
{
- using (_pm_OnPrePhysicsSimulation.Auto())
- OnPrePhysicsSimulation?.Invoke(tickDelta);
- using (_pm_PhysicsSimulate.Auto())
- Physics.Simulate(tickDelta);
- using (_pm_Physics2DSimulate.Auto())
- Physics2D.Simulate(tickDelta);
- using (_pm_OnPostPhysicsSimulation.Auto())
- OnPostPhysicsSimulation?.Invoke(tickDelta);
+ // Tell predicted objecs to reconcile before OnTick.
+ NetworkManager.PredictionManager.ReconcileToStates();
+
+ using (_pm_OnTick.Auto())
+ OnTick?.Invoke();
+
+ if (PhysicsMode == PhysicsMode.TimeManager && tickDelta > 0f)
+ {
+ using (_pm_OnPrePhysicsSimulation.Auto())
+ OnPrePhysicsSimulation?.Invoke(tickDelta);
+ using (_pm_PhysicsSimulate.Auto())
+ Physics.Simulate(tickDelta);
+ using (_pm_Physics2DSimulate.Auto())
+ Physics2D.Simulate(tickDelta);
+ using (_pm_OnPostPhysicsSimulation.Auto())
+ OnPostPhysicsSimulation?.Invoke(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.
@@ -795,12 +800,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();
@@ -811,6 +818,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;
@@ -892,6 +900,7 @@ public double TicksToTime(TickType tickType = TickType.LocalTick)
///
/// PreciseTick to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public double TicksToTime(PreciseTick pt)
{
double tickTime = TicksToTime(pt.Tick);
@@ -904,6 +913,7 @@ public double TicksToTime(PreciseTick pt)
///
/// Ticks to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public double TicksToTime(uint ticks)
{
return TickDelta * (double)ticks;
@@ -993,16 +1003,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);
}
///
@@ -1010,6 +1032,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;
@@ -1021,6 +1044,7 @@ public uint TimeToTicks(long time, TickRounding rounding = TickRounding.RoundNea
///
/// Time to convert.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public PreciseTick TimeToPreciseTick(double time) => time.AsPreciseTick(TickDelta);
///
diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
index d1ca6a57..8f947191 100644
--- a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
+++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs
@@ -122,7 +122,7 @@ public LatencySimulator LatencySimulator
private int _customMtuReserve = MINIMUM_MTU_RESERVE;
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
#endregion
#region Consts.
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 29b3530b..fd455750 100644
--- a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs
@@ -1,6 +1,7 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
#define DEVELOPMENT
#endif
+using System;
using FishNet.CodeGenerating;
using FishNet.Documenting;
using FishNet.Managing.Transporting;
@@ -76,7 +77,7 @@ public byte ComponentIndex
#if !UNITY_SERVER
///
///
- private NetworkTrafficStatistics _networkTrafficStatistics;
+ [NonSerialized] private NetworkTrafficStatistics _networkTrafficStatistics;
///
/// Name of this NetworkBehaviour.
///
@@ -101,7 +102,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..6d6d0f4e 100644
--- a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs
+++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs
@@ -7,6 +7,12 @@ namespace FishNet.Object
{
public partial class NetworkObject : MonoBehaviour
{
+ #region Types
+
+ public delegate void NetworkObjectCallback(NetworkObject nb);
+
+ #endregion
+
#region Private.
///
/// True if OnStartServer was called.
@@ -16,6 +22,40 @@ public partial class NetworkObject : MonoBehaviour
/// True if OnStartClient was called.
///
private bool _onStartClientCalled;
+
+ private bool OnStartServerCalled
+ {
+ get => _onStartServerCalled;
+ set
+ {
+ if (_onStartServerCalled != value)
+ {
+ _onStartServerCalled = value;
+ if (value) OnStartServerEvent?.Invoke(this);
+ else OnStopServerEvent?.Invoke(this);
+ }
+ }
+ }
+
+ private bool OnStartClientCalled
+ {
+ get => _onStartClientCalled;
+ set
+ {
+ if (_onStartClientCalled != value)
+ {
+ _onStartClientCalled = value;
+ if (value) OnStartClientEvent?.Invoke(this);
+ else OnStopClientEvent?.Invoke(this);
+ }
+ }
+ }
+
+ public event NetworkObjectCallback OnStartServerEvent;
+ public event NetworkObjectCallback OnStopServerEvent;
+ public event NetworkObjectCallback OnStartClientEvent;
+ public event NetworkObjectCallback OnStopClientEvent;
+
#endregion
// ReSharper disable Unity.PerformanceAnalysis
@@ -41,7 +81,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,7 +90,7 @@ 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);
}
@@ -114,17 +154,17 @@ internal void InvokeStopCallbacks(bool asServer, bool invokeSyncTypeCallbacks)
if (invokeSyncTypeCallbacks)
InvokeOnStopSyncTypeCallbacks(asServer);
- 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,10 +173,10 @@ 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;
}
void InvokeOnNetwork()
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/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;
}
}