// Remove on V5 // using FishNet.Managing; // using FishNet.Managing.Timing; // using FishNet.Utility.Extension; // using GameKit.Dependencies.Utilities; // using System.Runtime.CompilerServices; // using UnityEngine; // using UnityEngine.Scripting; // // namespace FishNet.Object.Prediction // { // /// // /// This class is under regular development and it's API may change at any time. // /// // public sealed class ChildTransformTickSmoother : IResettable // { // #region Types. // [Preserve] // private struct TickTransformProperties // { // public uint Tick; // public TransformProperties Properties; // // public TickTransformProperties(uint tick, Transform t) // { // Tick = tick; // Properties = new TransformProperties(t.localPosition, t.localRotation, t.localScale); // } // public TickTransformProperties(uint tick, Transform t, Vector3 localScale) // { // Tick = tick; // Properties = new TransformProperties(t.localPosition, t.localRotation, localScale); // } // public TickTransformProperties(uint tick, TransformProperties tp) // { // Tick = tick; // Properties = tp; // } // } // #endregion // // #region Private. // /// // /// Object to smooth. // /// // private Transform _graphicalObject; // /// // /// When not MoveRates.UNSET_VALUE the graphical object will teleport into it's next position if the move distance exceeds this value. // /// // private float _teleportThreshold; // /// // /// How quickly to move towards goal values. // /// // private MoveRates _moveRates = new MoveRates(MoveRates.UNSET_VALUE); // /// // /// True if a pretick occurred since last postTick. // /// // private bool _preTicked; // /// // /// World offset values of the graphical from the NetworkObject during initialization. // /// // private TransformProperties _gfxInitializedOffsetValues; // /// // /// World values of the graphical after it's been aligned to initialized values in PreTick. // /// // private TransformProperties _gfxPreSimulateWorldValues; // /// // /// TickDelta on the TimeManager. // /// // private float _tickDelta; // /// // /// How many ticks to interpolate over when not using adaptive. // /// // private byte _ownerInterpolation; // /// // /// Current interpolation, regardless of if using adaptive or not. // /// // private byte _interpolation; // /// // /// NetworkObject this is for. // /// // private NetworkObject _networkObject; // /// // /// Value to multiply movement by. This is used to reduce or increase the rate the movement buffer is consumed. // /// // private float _movementMultiplier = 1f; // /// // /// TransformProperties to move towards. // /// // private BasicQueue _transformProperties; // /// // /// Which properties to smooth. // /// // private TransformPropertiesFlag _ownerSmoothedProperties; // /// // /// Which properties to smooth. // /// // private TransformPropertiesFlag _spectatorSmoothedProperties; // /// // /// Updates the smoothedProperties value. // /// // /// New value. // /// True if updating values for the spectator, false if updating for owner. // public void SetSmoothedProperties(TransformPropertiesFlag value, bool forSpectator) // { // if (forSpectator) // _spectatorSmoothedProperties = value; // else // _ownerSmoothedProperties = value; // } // /// // /// Amount of adaptive interpolation to use. // /// // private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.Low; // /// // /// Updates the adaptiveInterpolation value. // /// // /// New value. // public void SetAdaptiveInterpolation(AdaptiveInterpolationType adaptiveInterpolation) // { // if (adaptiveInterpolation != AdaptiveInterpolationType.Off) // { // adaptiveInterpolation = AdaptiveInterpolationType.Off; // Debug.Log($"AdaptiveInterpolation has been changed to off at runtime while it's under development. This message may be ignored."); // } // _adaptiveInterpolation = adaptiveInterpolation; // } // /// // /// Set interpolation to use for spectated objects if adaptiveInterpolation is off. // /// // private byte _spectatorInterpolation; // /// // /// Sets the spectator interpolation value. // /// // /// New value. // /// True to also disable adaptive interpolation to use this new value. // public void SetSpectatorInterpolation(byte value, bool disableAdaptiveInterpolation = true) // { // _spectatorInterpolation = value; // if (disableAdaptiveInterpolation) // _adaptiveInterpolation = AdaptiveInterpolationType.Off; // } // /// // /// Previous parent the graphical was attached to. // /// // private Transform _previousParent; // /// // /// True if to detach at runtime. // /// // private bool _detach; // /// // /// True if were an owner of the NetworkObject during PreTick. // /// This is only used for performance gains. // /// // private bool _ownerOnPretick; // /// // /// True if adaptive interpolation should be used. // /// // private bool _useAdaptiveInterpolation => (!_ownerOnPretick && _adaptiveInterpolation != AdaptiveInterpolationType.Off); // /// // /// True if Initialized has been called and settings have not been reset. // /// // private bool _initialized; // #endregion // // #region Const. // /// // /// Maximum allowed entries to be queued over the interpolation amount. // /// // private int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3; // #endregion // // ~ChildTransformTickSmoother() // { // //This is a last resort for if something didnt deinitialize right. // ResetState(); // } // // /// // /// Initializes this smoother; should only be completed once. // /// // public void Initialize(NetworkObject nob, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, byte spectatorInterpolation, TransformPropertiesFlag specatorSmoothedProperties, AdaptiveInterpolationType adaptiveInterpolation) // { // ResetState(); // _detach = detach; // _networkObject = nob; // _transformProperties = CollectionCaches.RetrieveBasicQueue(); // _gfxInitializedOffsetValues = nob.transform.GetTransformOffsets(graphicalObject); // _tickDelta = tickDelta; // _graphicalObject = graphicalObject; // _teleportThreshold = teleportDistance; // _ownerInterpolation = ownerInterpolation; // _spectatorInterpolation = spectatorInterpolation; // _ownerSmoothedProperties = ownerSmoothedProperties; // _spectatorSmoothedProperties = specatorSmoothedProperties; // SetAdaptiveInterpolation(adaptiveInterpolation); // UpdateInterpolation(0); // _initialized = true; // } // // /// // /// Deinitializes this smoother resetting values. // /// // public void Deinitialize() // { // ResetState(); // } // // /// // /// Updates interpolation based on localClient latency. // /// // private void UpdateInterpolation(uint clientStateTick) // { // if (_networkObject.IsServerStarted || _networkObject.IsOwner) // { // _interpolation = _ownerInterpolation; // } // else // { // if (_adaptiveInterpolation == AdaptiveInterpolationType.Off) // { // _interpolation = _spectatorInterpolation; // } // else // { // float interpolation; // TimeManager tm = _networkObject.TimeManager; // if (clientStateTick == 0) // { // //Not enough data to calculate; guestimate. This should only happen once. // float fRtt = (float)tm.RoundTripTime; // interpolation = (fRtt / 10f); // // } // else // { // interpolation = (tm.LocalTick - clientStateTick) + _networkObject.PredictionManager.StateInterpolation; // } // // switch (_adaptiveInterpolation) // { // case AdaptiveInterpolationType.VeryLow: // interpolation *= 0.25f; // break; // case AdaptiveInterpolationType.Low: // interpolation *= 0.375f; // break; // case AdaptiveInterpolationType.Medium: // interpolation *= 0.5f; // break; // case AdaptiveInterpolationType.High: // interpolation *= 0.75f; // break; // //Make no changes for maximum. // } // // interpolation = Mathf.Clamp(interpolation, 1f, (float)byte.MaxValue); // _interpolation = (byte)Mathf.RoundToInt(interpolation); // } // } // } // // internal void OnStartClient() // { // if (!_detach) // return; // // _previousParent = _graphicalObject.parent; // TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); // _graphicalObject.SetParent(null); // _graphicalObject.SetWorldProperties(gfxWorldProperties); // } // // internal void OnStopClient() // { // if (!_detach || _previousParent == null || _graphicalObject == null) // return; // // _graphicalObject.SetParent(_previousParent); // _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); // } // // /// // /// Called every frame. // /// // internal void Update() // { // if (!CanSmooth()) // return; // // if (_useAdaptiveInterpolation) // AdaptiveMoveToTarget(Time.deltaTime); // else // BasicMoveToTarget(Time.deltaTime); // } // // /// // /// Called when the TimeManager invokes OnPreTick. // /// // public void OnPreTick() // { // if (!CanSmooth()) // return; // // _preTicked = true; // // _ownerOnPretick = _networkObject.IsOwner; // if (_useAdaptiveInterpolation) // DiscardExcessiveTransformPropertiesQueue(); // else // ClearTransformPropertiesQueue(); // //These only need to be set if still attached. // if (!_detach) // _gfxPreSimulateWorldValues = _graphicalObject.GetWorldProperties(); // } // // /// // /// Called when the PredictionManager invokes OnPreReconcile. // /// // public void OnPreReconcile() // { // UpdateInterpolation(_networkObject.PredictionManager.ClientStateTick); // } // // /// // /// Called when the TimeManager invokes OnPostReplay. // /// // /// Replay tick for the local client. // public void OnPostReplay(uint clientTick) // { // if (_transformProperties.Count == 0) // return; // if (!_useAdaptiveInterpolation) // return; // // uint firstTick = _transformProperties.Peek().Tick; // //Already in motion to first entry, or first entry passed tick. // if (clientTick <= firstTick) // return; // // ModifyTransformProperties(clientTick, firstTick); // } // // /// // /// Called when TimeManager invokes OnPostTick. // /// // /// Local tick of the client. // public void OnPostTick(uint clientTick) // { // if (!CanSmooth()) // return; // // //If preticked then previous transform values are known. // if (_preTicked) // { // if (_useAdaptiveInterpolation) // DiscardExcessiveTransformPropertiesQueue(); // else // ClearTransformPropertiesQueue(); // //Only needs to be put to pretick position if not detached. // if (!_detach) // _graphicalObject.SetWorldProperties(_gfxPreSimulateWorldValues); // AddTransformProperties(clientTick); // } // //If did not pretick then the only thing we can do is snap to instantiated values. // else // { // //Only set to position if not to detach. // if (!_detach) // _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); // } // } // // /// // /// Teleports the graphical to it's starting position and clears the internal movement queue. // /// // public void Teleport() // { // ClearTransformPropertiesQueue(); // TransformProperties startProperties = _networkObject.transform.GetWorldProperties(); // startProperties.Add(_gfxInitializedOffsetValues); // _graphicalObject.SetWorldProperties(startProperties); // } // // /// // /// Clears the pending movement queue. // /// // private void ClearTransformPropertiesQueue() // { // _transformProperties.Clear(); // //Also unset move rates since there is no more queue. // _moveRates = new MoveRates(MoveRates.UNSET_VALUE); // } // // /// // /// Discards datas over interpolation limit from movement queue. // /// // private void DiscardExcessiveTransformPropertiesQueue() // { // if (!_useAdaptiveInterpolation) // { // _networkObject.NetworkManager.LogError($"This method should only be called when using adaptive interpolation."); // return; // } // // int dequeueCount = (_transformProperties.Count - (_interpolation + MAXIMUM_QUEUED_OVER_INTERPOLATION)); // //If there are entries to dequeue. // if (dequeueCount > 0) // { // TickTransformProperties tpp = default; // for (int i = 0; i < dequeueCount; i++) // tpp = _transformProperties.Dequeue(); // // SetAdaptiveMoveRates(tpp.Properties, _transformProperties[0].Properties); // } // } // // /// // /// Adds a new transform properties and sets move rates if needed. // /// // private void AddTransformProperties(uint tick) // { // TickTransformProperties tpp = new TickTransformProperties(tick, GetNetworkObjectWorldPropertiesWithOffset()); // // _transformProperties.Enqueue(tpp); // //If first entry then set move rates. // if (_transformProperties.Count == 1) // { // TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); // if (_useAdaptiveInterpolation) // SetAdaptiveMoveRates(gfxWorldProperties, tpp.Properties); // else // SetBasicMoveRates(gfxWorldProperties, tpp.Properties); // } // } // // /// // /// Modifies a transform property for a tick. This does not error check for empty collections. // /// // /// First tick in the queue. If 0 this will be looked up. // private void ModifyTransformProperties(uint clientTick, uint firstTick) // { // uint tick = clientTick; // /*Ticks will always be added incremental by 1 so it's safe to jump ahead the difference // * of tick and firstTick. */ // int index = (int)(tick - firstTick); // //Replace with new data. // if (index < _transformProperties.Count) // { // _transformProperties[index] = new TickTransformProperties(tick, _networkObject.transform, _graphicalObject.localScale); // } // else // { // //This should never happen. // } // } // // /// // /// Returns TransformProperties of the NetworkObject with the graphicals world offset. // /// // /// // private TransformProperties GetNetworkObjectWorldPropertiesWithOffset() => _networkObject.transform.GetWorldProperties(_gfxInitializedOffsetValues); // // /// // /// Returns if prediction can be used on this rigidbody. // /// // /// // private bool CanSmooth() // { // if (_graphicalObject == null) // return false; // // return true; // } // // /// // /// Sets Position and Rotation move rates to reach Target datas. // /// // private void SetBasicMoveRates(TransformProperties prevValues, TransformProperties nextValues) // { // byte interpolation = _interpolation; // float duration = (_tickDelta * interpolation); // /* If interpolation is 1 then add on a tiny amount // * of more time to compensate for frame time, so that // * the smoothing does not complete before the next tick, // * as this would result in jitter. */ // if (interpolation == 1) // duration += (1 / 55f); // float teleportT = (_teleportThreshold * (float)interpolation); // // _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT); // _moveRates.TimeRemaining = duration; // } // // // /// // /// Sets Position and Rotation move rates to reach Target datas. // /// // private void SetAdaptiveMoveRates(TransformProperties prevValues, TransformProperties nextValues) // { // float duration = _tickDelta; // /* If interpolation is 1 then add on a tiny amount // * of more time to compensate for frame time, so that // * the smoothing does not complete before the next tick, // * as this would result in jitter. */ // float teleportT = _teleportThreshold; // _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT); // _moveRates.TimeRemaining = duration; // // SetMovementMultiplier(); // } // // private void SetMovementMultiplier() // { // /* If there's more in queue than interpolation then begin to move faster based on overage. // * Move 5% faster for every overage. */ // int overInterpolation = (_transformProperties.Count - _interpolation); // //If needs to be adjusted. // if (overInterpolation != 0f) // { // _movementMultiplier += (0.015f * overInterpolation); // } // //If does not need to be adjusted. // else // { // //If interpolation is 1 then slow down just barely to accomodate for frame delta variance. // if (_interpolation == 1) // _movementMultiplier = 0.99f; // } // // _movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.95f, 1.05f); // } // // // /// // /// Moves transform to target values. // /// // // private void BasicMoveToTarget(float delta) // { // int tpCount = _transformProperties.Count; // //No data. // if (tpCount == 0) // return; // // TickTransformProperties ttp = _transformProperties.Peek(); // _moveRates.MoveWorldToTarget(_graphicalObject, ttp.Properties, delta); // // //if TimeLeft is <= 0f then transform should be at goal. // if (_moveRates.TimeRemaining <= 0f) // ClearTransformPropertiesQueue(); // } // // /// // /// Moves transform to target values. // /// // // private void AdaptiveMoveToTarget(float delta) // { // int tpCount = _transformProperties.Count; // //No data. // if (tpCount == 0) // return; // /* If buffer is considerably under goal then halt // * movement. This will allow the buffer to grow. */ // if ((tpCount - _interpolation) < -4) // return; // // TickTransformProperties ttp = _transformProperties.Peek(); // TransformPropertiesFlag smoothedProperties = (_ownerOnPretick) ? _ownerSmoothedProperties : _spectatorSmoothedProperties; // _moveRates.MoveWorldToTarget(_graphicalObject, ttp.Properties, smoothedProperties, (delta * _movementMultiplier)); // float tRemaining = _moveRates.TimeRemaining; // //if TimeLeft is <= 0f then transform is at goal. Grab a new goal if possible. // if (tRemaining <= 0f) // { // //Dequeue current entry and if there's another call a move on it. // _transformProperties.Dequeue(); // // //If there are entries left then setup for the next. // if (_transformProperties.Count > 0) // { // SetAdaptiveMoveRates(ttp.Properties, _transformProperties.Peek().Properties); // //If delta is negative then call move again with abs. // if (tRemaining < 0f) // AdaptiveMoveToTarget(Mathf.Abs(tRemaining)); // } // //No remaining, set to snap. // else // { // ClearTransformPropertiesQueue(); // } // } // } // // public void ResetState() // { // if (!_initialized) // return; // // _networkObject = null; // if (_graphicalObject != null) // { // if (_networkObject != null) // { // if (_detach) // _graphicalObject.SetParent(_networkObject.transform); // _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); // _graphicalObject = null; // } // else if (_detach) // { // UnityEngine.Object.Destroy(_graphicalObject.gameObject); // } // } // _movementMultiplier = 1f; // CollectionCaches.StoreAndDefault(ref _transformProperties); // _teleportThreshold = default; // _moveRates = default; // _preTicked = default; // _gfxInitializedOffsetValues = default; // _gfxPreSimulateWorldValues = default; // _tickDelta = default; // _interpolation = default; // } // // public void InitializeState() { } // } // // }