Unity-WebSocket/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs
2025-06-28 11:28:54 +03:30

629 lines
26 KiB
C#

// 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
// {
// /// <summary>
// /// This class is under regular development and it's API may change at any time.
// /// </summary>
// 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.
// /// <summary>
// /// Object to smooth.
// /// </summary>
// private Transform _graphicalObject;
// /// <summary>
// /// When not MoveRates.UNSET_VALUE the graphical object will teleport into it's next position if the move distance exceeds this value.
// /// </summary>
// private float _teleportThreshold;
// /// <summary>
// /// How quickly to move towards goal values.
// /// </summary>
// private MoveRates _moveRates = new MoveRates(MoveRates.UNSET_VALUE);
// /// <summary>
// /// True if a pretick occurred since last postTick.
// /// </summary>
// private bool _preTicked;
// /// <summary>
// /// World offset values of the graphical from the NetworkObject during initialization.
// /// </summary>
// private TransformProperties _gfxInitializedOffsetValues;
// /// <summary>
// /// World values of the graphical after it's been aligned to initialized values in PreTick.
// /// </summary>
// private TransformProperties _gfxPreSimulateWorldValues;
// /// <summary>
// /// TickDelta on the TimeManager.
// /// </summary>
// private float _tickDelta;
// /// <summary>
// /// How many ticks to interpolate over when not using adaptive.
// /// </summary>
// private byte _ownerInterpolation;
// /// <summary>
// /// Current interpolation, regardless of if using adaptive or not.
// /// </summary>
// private byte _interpolation;
// /// <summary>
// /// NetworkObject this is for.
// /// </summary>
// private NetworkObject _networkObject;
// /// <summary>
// /// Value to multiply movement by. This is used to reduce or increase the rate the movement buffer is consumed.
// /// </summary>
// private float _movementMultiplier = 1f;
// /// <summary>
// /// TransformProperties to move towards.
// /// </summary>
// private BasicQueue<TickTransformProperties> _transformProperties;
// /// <summary>
// /// Which properties to smooth.
// /// </summary>
// private TransformPropertiesFlag _ownerSmoothedProperties;
// /// <summary>
// /// Which properties to smooth.
// /// </summary>
// private TransformPropertiesFlag _spectatorSmoothedProperties;
// /// <summary>
// /// Updates the smoothedProperties value.
// /// </summary>
// /// <param name="value">New value.</param>
// /// <param name="forSpectator">True if updating values for the spectator, false if updating for owner.</param>
// public void SetSmoothedProperties(TransformPropertiesFlag value, bool forSpectator)
// {
// if (forSpectator)
// _spectatorSmoothedProperties = value;
// else
// _ownerSmoothedProperties = value;
// }
// /// <summary>
// /// Amount of adaptive interpolation to use.
// /// </summary>
// private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.Low;
// /// <summary>
// /// Updates the adaptiveInterpolation value.
// /// </summary>
// /// <param name="adaptiveInterpolation">New value.</param>
// 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;
// }
// /// <summary>
// /// Set interpolation to use for spectated objects if adaptiveInterpolation is off.
// /// </summary>
// private byte _spectatorInterpolation;
// /// <summary>
// /// Sets the spectator interpolation value.
// /// </summary>
// /// <param name="value">New value.</param>
// /// <param name="disableAdaptiveInterpolation">True to also disable adaptive interpolation to use this new value.</param>
// public void SetSpectatorInterpolation(byte value, bool disableAdaptiveInterpolation = true)
// {
// _spectatorInterpolation = value;
// if (disableAdaptiveInterpolation)
// _adaptiveInterpolation = AdaptiveInterpolationType.Off;
// }
// /// <summary>
// /// Previous parent the graphical was attached to.
// /// </summary>
// private Transform _previousParent;
// /// <summary>
// /// True if to detach at runtime.
// /// </summary>
// private bool _detach;
// /// <summary>
// /// True if were an owner of the NetworkObject during PreTick.
// /// This is only used for performance gains.
// /// </summary>
// private bool _ownerOnPretick;
// /// <summary>
// /// True if adaptive interpolation should be used.
// /// </summary>
// private bool _useAdaptiveInterpolation => (!_ownerOnPretick && _adaptiveInterpolation != AdaptiveInterpolationType.Off);
// /// <summary>
// /// True if Initialized has been called and settings have not been reset.
// /// </summary>
// private bool _initialized;
// #endregion
//
// #region Const.
// /// <summary>
// /// Maximum allowed entries to be queued over the interpolation amount.
// /// </summary>
// private int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3;
// #endregion
//
// ~ChildTransformTickSmoother()
// {
// //This is a last resort for if something didnt deinitialize right.
// ResetState();
// }
//
// /// <summary>
// /// Initializes this smoother; should only be completed once.
// /// </summary>
// 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<TickTransformProperties>.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;
// }
//
// /// <summary>
// /// Deinitializes this smoother resetting values.
// /// </summary>
// public void Deinitialize()
// {
// ResetState();
// }
//
// /// <summary>
// /// Updates interpolation based on localClient latency.
// /// </summary>
// 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());
// }
//
// /// <summary>
// /// Called every frame.
// /// </summary>
// internal void Update()
// {
// if (!CanSmooth())
// return;
//
// if (_useAdaptiveInterpolation)
// AdaptiveMoveToTarget(Time.deltaTime);
// else
// BasicMoveToTarget(Time.deltaTime);
// }
//
// /// <summary>
// /// Called when the TimeManager invokes OnPreTick.
// /// </summary>
// 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();
// }
//
// /// <summary>
// /// Called when the PredictionManager invokes OnPreReconcile.
// /// </summary>
// public void OnPreReconcile()
// {
// UpdateInterpolation(_networkObject.PredictionManager.ClientStateTick);
// }
//
// /// <summary>
// /// Called when the TimeManager invokes OnPostReplay.
// /// </summary>
// /// <param name="clientTick">Replay tick for the local client.</param>
// 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);
// }
//
// /// <summary>
// /// Called when TimeManager invokes OnPostTick.
// /// </summary>
// /// <param name="clientTick">Local tick of the client.</param>
// 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());
// }
// }
//
// /// <summary>
// /// Teleports the graphical to it's starting position and clears the internal movement queue.
// /// </summary>
// public void Teleport()
// {
// ClearTransformPropertiesQueue();
// TransformProperties startProperties = _networkObject.transform.GetWorldProperties();
// startProperties.Add(_gfxInitializedOffsetValues);
// _graphicalObject.SetWorldProperties(startProperties);
// }
//
// /// <summary>
// /// Clears the pending movement queue.
// /// </summary>
// private void ClearTransformPropertiesQueue()
// {
// _transformProperties.Clear();
// //Also unset move rates since there is no more queue.
// _moveRates = new MoveRates(MoveRates.UNSET_VALUE);
// }
//
// /// <summary>
// /// Discards datas over interpolation limit from movement queue.
// /// </summary>
// 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);
// }
// }
//
// /// <summary>
// /// Adds a new transform properties and sets move rates if needed.
// /// </summary>
// 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);
// }
// }
//
// /// <summary>
// /// Modifies a transform property for a tick. This does not error check for empty collections.
// /// </summary>
// /// <param name="firstTick">First tick in the queue. If 0 this will be looked up.</param>
// 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.
// }
// }
//
// /// <summary>
// /// Returns TransformProperties of the NetworkObject with the graphicals world offset.
// /// </summary>
// /// <returns></returns>
// private TransformProperties GetNetworkObjectWorldPropertiesWithOffset() => _networkObject.transform.GetWorldProperties(_gfxInitializedOffsetValues);
//
// /// <summary>
// /// Returns if prediction can be used on this rigidbody.
// /// </summary>
// /// <returns></returns>
// private bool CanSmooth()
// {
// if (_graphicalObject == null)
// return false;
//
// return true;
// }
//
// /// <summary>
// /// Sets Position and Rotation move rates to reach Target datas.
// /// </summary>
// 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;
// }
//
//
// /// <summary>
// /// Sets Position and Rotation move rates to reach Target datas.
// /// </summary>
// 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);
// }
//
//
// /// <summary>
// /// Moves transform to target values.
// /// </summary>
//
// 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();
// }
//
// /// <summary>
// /// Moves transform to target values.
// /// </summary>
//
// 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<TickTransformProperties>.StoreAndDefault(ref _transformProperties);
// _teleportThreshold = default;
// _moveRates = default;
// _preTicked = default;
// _gfxInitializedOffsetValues = default;
// _gfxPreSimulateWorldValues = default;
// _tickDelta = default;
// _interpolation = default;
// }
//
// public void InitializeState() { }
// }
//
// }