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; } }