[Add] FishNet
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// namespace FishNet.Component.Transforming
|
||||
// {
|
||||
// public enum AdaptiveInterpolationType
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// Adaptive interpolation is disabled. An exact interpolation value is used.
|
||||
// /// </summary>
|
||||
// Off = 0,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are definite without predicting future states.
|
||||
// /// </summary>
|
||||
// ExtremelyLow = 1,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are likely without predicting future states.
|
||||
// /// </summary>
|
||||
// VeryLow = 2,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are still possible but less likely.
|
||||
// /// </summary>
|
||||
// Low = 3,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are likely without predicting a small amount of future states.
|
||||
// /// </summary>
|
||||
// Moderate = 4,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are very unlikely. Graphics are using a generous amount interpolation.
|
||||
// /// </summary>
|
||||
// High = 5,
|
||||
// /// <summary>
|
||||
// /// Visual disturbances caused by desynchronization are extremely unlikely. Graphics are using a generous amount interpolation.
|
||||
// /// </summary>
|
||||
// VeryHigh = 6,
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9aaf062df354644098da9c46e2a9512
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,630 @@
|
||||
// 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() { }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50721ce3e3600754dbc3c6e2e5446728
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace FishNet.Utility
|
||||
{
|
||||
internal static class UtilityConstants
|
||||
{
|
||||
public const string CODEGEN_ASSEMBLY_NAME = "Unity.FishNet.CodeGen";
|
||||
public const string GENERATED_ASSEMBLY_NAME = "FishNet.Generated";
|
||||
public const string DEMOS_ASSEMBLY_NAME = "FishNet.Demos";
|
||||
public const string TEST_ASSEMBLY_NAME = "FishNet.Test";
|
||||
public const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime";
|
||||
public const string GeneratedWriterPrefix = "GWrite___";
|
||||
public const string GeneratedReaderPrefix = "GRead___";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f2a3c23b44e4ef4e9783ef53ec0d5da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Constants.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d008bcb408806c45980331a665c65a7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Object;
|
||||
|
||||
namespace FishNet.Utility.Extension
|
||||
{
|
||||
[APIExclude]
|
||||
public static class NetworksFN
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
|
||||
/// </summary>
|
||||
public static bool DoubleLogic(this NetworkObject nob, bool asServer) => !asServer && nob.NetworkManager.IsServerStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
|
||||
/// </summary>
|
||||
public static bool DoubleLogic(this NetworkManager manager, bool asServer) => !asServer && manager.IsServerStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side.
|
||||
/// </summary>
|
||||
public static bool DoubleLogic(this NetworkBehaviour nb, bool asServer) => !asServer && nb.NetworkManager.IsServerStarted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbd3adaae5d34a14ab9cf68726b3bd3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Extension/Networks.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,90 @@
|
||||
using FishNet.Managing;
|
||||
using FishNet.Object;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace FishNet.Utility.Extension
|
||||
{
|
||||
public static class Scenes
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all NetworkObjects in a scene.
|
||||
/// </summary>
|
||||
/// <param name = "s">Scene to get objects in.</param>
|
||||
/// <param name = "firstOnly">True to only return the first NetworkObject within an object chain. False will return nested NetworkObjects.</param>
|
||||
/// <returns></returns>
|
||||
public static void GetSceneNetworkObjects(Scene s, bool firstOnly, bool errorOnDuplicates, bool ignoreUnsetSceneIds, ref List<NetworkObject> result)
|
||||
{
|
||||
List<NetworkObject> nobCacheA = CollectionCaches<NetworkObject>.RetrieveList();
|
||||
List<NetworkObject> nobCacheB = CollectionCaches<NetworkObject>.RetrieveList();
|
||||
List<GameObject> gameObjectCache = CollectionCaches<GameObject>.RetrieveList();
|
||||
Dictionary<ulong, NetworkObject> sceneIds = CollectionCaches<ulong, NetworkObject>.RetrieveDictionary();
|
||||
|
||||
// Iterate all root objects for the scene.
|
||||
s.GetRootGameObjects(gameObjectCache);
|
||||
foreach (GameObject go in gameObjectCache)
|
||||
{
|
||||
// Get NetworkObjects within children of each root.
|
||||
go.GetComponentsInChildren(true, nobCacheA);
|
||||
// If network objects are found.
|
||||
if (nobCacheA.Count > 0)
|
||||
{
|
||||
// Add only the first networkobject
|
||||
if (firstOnly)
|
||||
{
|
||||
/* The easiest way to see if a nob is nested is to
|
||||
* get nobs in parent and if the count is greater than 1, then
|
||||
* it is nested. The technique used here isn't exactly fast but
|
||||
* it will only occur during scene loads, so I'm trading off speed
|
||||
* for effort and readability. */
|
||||
foreach (NetworkObject nob in nobCacheA)
|
||||
{
|
||||
if (ignoreUnsetSceneIds && !nob.IsSceneObject)
|
||||
continue;
|
||||
|
||||
nob.GetComponentsInParent(true, nobCacheB);
|
||||
// No extra nobs, only this one.
|
||||
if (nobCacheB.Count == 1 && !TryDisplayDuplicateError(nob))
|
||||
result.Add(nob);
|
||||
}
|
||||
}
|
||||
// Not first only, add them all.
|
||||
else
|
||||
{
|
||||
foreach (NetworkObject item in nobCacheA)
|
||||
{
|
||||
if (ignoreUnsetSceneIds && !item.IsSceneObject)
|
||||
continue;
|
||||
if (!TryDisplayDuplicateError(item))
|
||||
result.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CollectionCaches<ulong, NetworkObject>.Store(sceneIds);
|
||||
|
||||
bool TryDisplayDuplicateError(NetworkObject nob)
|
||||
{
|
||||
if (!errorOnDuplicates)
|
||||
return false;
|
||||
|
||||
ulong id = nob.SceneId;
|
||||
// There is a duplicate.
|
||||
if (sceneIds.TryGetValue(id, out NetworkObject originalNob))
|
||||
{
|
||||
string err = $"Object {nob.name} and {originalNob.name} in scene {nob.gameObject.scene.name} have the same sceneId of {id}. This will result in spawning errors. Exit play mode and use the Fish-Networking menu to reserialize sceneIds for scene {nob.gameObject.scene.name}.";
|
||||
NetworkManagerExtensions.LogError(err);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sceneIds[id] = nob;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a02f3d03f737e304e9854278f4e9211d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Extension/Scenes.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using FishNet.Documenting;
|
||||
using FishNet.Object;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Utility.Extension
|
||||
{
|
||||
[APIExclude]
|
||||
public static class TransformFN
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets values of TransformProperties to a transforms world properties.
|
||||
/// </summary>
|
||||
public static TransformProperties GetWorldProperties(this Transform t)
|
||||
{
|
||||
TransformProperties tp = new(t.position, t.rotation, t.localScale);
|
||||
return tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets values of TransformProperties to a transforms world properties.
|
||||
/// </summary>
|
||||
public static TransformProperties GetWorldProperties(this Transform t, TransformProperties offset)
|
||||
{
|
||||
TransformProperties tp = new(t.position, t.rotation, t.localScale);
|
||||
tp.Add(offset);
|
||||
return tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets values of TransformProperties to a transforms world properties.
|
||||
/// </summary>
|
||||
public static TransformPropertiesCls GetWorldPropertiesCls(this Transform t)
|
||||
{
|
||||
TransformPropertiesCls tp = new(t.position, t.rotation, t.localScale);
|
||||
return tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets values of TransformProperties to a transforms world properties.
|
||||
/// </summary>
|
||||
public static TransformProperties GetLocalProperties(this Transform t)
|
||||
{
|
||||
TransformProperties tp = new(t.localPosition, t.localRotation, t.localScale);
|
||||
return tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets values of TransformProperties to a transforms world properties.
|
||||
/// </summary>
|
||||
public static TransformPropertiesCls GetLocalPropertiesCls(this Transform t)
|
||||
{
|
||||
TransformPropertiesCls tp = new(t.localPosition, t.localRotation, t.localScale);
|
||||
return tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets values of TransformPropertiesCls to a transforms world properties.
|
||||
/// </summary>
|
||||
[Obsolete("Use TransformPropertiesExtensions.SetWorldProperties.")]
|
||||
public static void SetWorldProperties(this TransformPropertiesCls tp, Transform t) => TransformPropertiesExtensions.SetWorldProperties(tp, t);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset values by subtracting this from target.
|
||||
/// </summary>
|
||||
/// <param name = "pos">Position offset result.</param>
|
||||
/// <param name = "rot">Rotation offset result.</param>
|
||||
public static void SetTransformOffsets(this Transform t, Transform target, ref Vector3 pos, ref Quaternion rot)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
pos = target.position - t.position;
|
||||
rot = target.rotation * Quaternion.Inverse(t.rotation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the offset values by subtracting this from target.
|
||||
/// </summary>
|
||||
/// <param name = "zeroScale">True to set scale to Vector3.zero.</param>
|
||||
public static TransformProperties GetTransformOffsets(this Transform t, Transform target)
|
||||
{
|
||||
if (target == null)
|
||||
return default;
|
||||
|
||||
return new(target.position - t.position, target.rotation * Quaternion.Inverse(t.rotation), target.localScale - t.localScale);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform to local properties.
|
||||
/// </summary>
|
||||
public static void SetLocalProperties(this Transform t, TransformPropertiesCls tp)
|
||||
{
|
||||
t.localPosition = tp.Position;
|
||||
t.localRotation = tp.Rotation;
|
||||
t.localScale = tp.LocalScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform to local properties.
|
||||
/// </summary>
|
||||
public static void SetLocalProperties(this Transform t, TransformProperties tp)
|
||||
{
|
||||
t.localPosition = tp.Position;
|
||||
t.localRotation = tp.Rotation;
|
||||
t.localScale = tp.Scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform to world properties.
|
||||
/// </summary>
|
||||
public static void SetWorldProperties(this Transform t, TransformPropertiesCls tp)
|
||||
{
|
||||
t.position = tp.Position;
|
||||
t.rotation = tp.Rotation;
|
||||
t.localScale = tp.LocalScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a transform to world properties.
|
||||
/// </summary>
|
||||
public static void SetWorldProperties(this Transform t, TransformProperties tp)
|
||||
{
|
||||
t.position = tp.Position;
|
||||
t.rotation = tp.Rotation;
|
||||
t.localScale = tp.Scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets local position and rotation for a transform.
|
||||
/// </summary>
|
||||
public static void SetLocalPositionAndRotation(this Transform t, Vector3 pos, Quaternion rot)
|
||||
{
|
||||
t.localPosition = pos;
|
||||
t.localRotation = rot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets local position, rotation, and scale for a transform.
|
||||
/// </summary>
|
||||
public static void SetLocalPositionRotationAndScale(this Transform t, Vector3 pos, Quaternion rot, Vector3 scale)
|
||||
{
|
||||
t.localPosition = pos;
|
||||
t.localRotation = rot;
|
||||
t.localScale = scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets local position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped.
|
||||
/// </summary>
|
||||
public static void SetLocalPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
|
||||
{
|
||||
if (nullablePos.HasValue)
|
||||
t.localPosition = nullablePos.Value;
|
||||
if (nullableRot.HasValue)
|
||||
t.localRotation = nullableRot.Value;
|
||||
if (nullableScale.HasValue)
|
||||
t.localScale = nullableScale.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets world position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped.
|
||||
/// </summary>
|
||||
public static void SetWorldPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale)
|
||||
{
|
||||
if (nullablePos.HasValue)
|
||||
t.position = nullablePos.Value;
|
||||
if (nullableRot.HasValue)
|
||||
t.rotation = nullableRot.Value;
|
||||
if (nullableScale.HasValue)
|
||||
t.localScale = nullableScale.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used.
|
||||
/// </summary>
|
||||
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;
|
||||
scale = nullableScale == null ? t.localScale : nullableScale.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used.
|
||||
/// </summary>
|
||||
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;
|
||||
scale = nullableScale == null ? t.localScale : nullableScale.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d311fc1bf09b9e4fbc5a17a9c50ab0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Extension/Transforms.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using FishNet.Utility.Extension;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Object.Prediction
|
||||
{
|
||||
[Obsolete("This class will be removed in version 5.")]
|
||||
internal class LocalTransformTickSmoother : IResettable
|
||||
{
|
||||
#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;
|
||||
/// <summary>
|
||||
/// True if a pretick occurred since last postTick.
|
||||
/// </summary>
|
||||
private bool _preTicked;
|
||||
/// <summary>
|
||||
/// Local values of the graphical during pretick.
|
||||
/// </summary>
|
||||
private TransformProperties _gfxInitializedLocalValues;
|
||||
/// <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.
|
||||
/// </summary>
|
||||
private byte _interpolation;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this smoother; should only be completed once.
|
||||
/// </summary>
|
||||
internal void InitializeOnce(Transform graphicalObject, float teleportDistance, float tickDelta, byte interpolation)
|
||||
{
|
||||
_gfxInitializedLocalValues = graphicalObject.GetLocalProperties();
|
||||
_tickDelta = tickDelta;
|
||||
_graphicalObject = graphicalObject;
|
||||
_teleportThreshold = teleportDistance * (float)interpolation;
|
||||
_interpolation = interpolation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame.
|
||||
/// </summary>
|
||||
internal void Update()
|
||||
{
|
||||
if (!CanSmooth())
|
||||
return;
|
||||
|
||||
MoveToTarget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the TimeManager invokes OnPreTick.
|
||||
/// </summary>
|
||||
internal void OnPreTick()
|
||||
{
|
||||
if (!CanSmooth())
|
||||
return;
|
||||
|
||||
_preTicked = true;
|
||||
_gfxPreSimulateWorldValues = _graphicalObject.GetWorldProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when TimeManager invokes OnPostTick.
|
||||
/// </summary>
|
||||
internal void OnPostTick()
|
||||
{
|
||||
if (!CanSmooth())
|
||||
return;
|
||||
|
||||
// If preticked then previous transform values are known.
|
||||
if (_preTicked)
|
||||
{
|
||||
_graphicalObject.SetWorldProperties(_gfxPreSimulateWorldValues);
|
||||
SetMoveRates(_gfxInitializedLocalValues, _graphicalObject);
|
||||
}
|
||||
// If did not pretick then the only thing we can do is snap to instantiated values.
|
||||
else
|
||||
{
|
||||
_graphicalObject.SetLocalProperties(_gfxInitializedLocalValues);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 SetMoveRates(TransformProperties prevValues, Transform t)
|
||||
{
|
||||
float duration = _tickDelta * (float)_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 += Mathf.Max(Time.deltaTime, 1f / 50f);
|
||||
float teleportT = _teleportThreshold;
|
||||
_moveRates = MoveRates.GetLocalMoveRates(prevValues, t, duration, teleportT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
private void MoveToTarget()
|
||||
{
|
||||
_moveRates.Move(_graphicalObject, _gfxInitializedLocalValues, Time.deltaTime, useWorldSpace: false);
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
if (_graphicalObject != null)
|
||||
{
|
||||
_graphicalObject.SetLocalProperties(_gfxInitializedLocalValues);
|
||||
_graphicalObject = null;
|
||||
}
|
||||
_teleportThreshold = default;
|
||||
_moveRates = default;
|
||||
_preTicked = default;
|
||||
_gfxInitializedLocalValues = default;
|
||||
_gfxPreSimulateWorldValues = default;
|
||||
_tickDelta = default;
|
||||
_interpolation = default;
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: facff21f41a83964fb8c0dc195d94646
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b315d9102b18b541b0b2346c9ad63ed
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,230 @@
|
||||
// using System; // Remove on V5
|
||||
|
||||
// namespace FishNet.Utility.Performance
|
||||
// {
|
||||
|
||||
// /// <summary>
|
||||
// /// Unity 2022 has a bug where codegen will not compile when referencing a Queue type,
|
||||
// /// while also targeting .Net as the framework API.
|
||||
// /// As a work around this class is used for queues instead.
|
||||
// /// </summary>
|
||||
// public class BasicQueue<T>
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// Maximum size of the collection.
|
||||
// /// </summary>
|
||||
// public int Capacity => Collection.Length;
|
||||
// /// <summary>
|
||||
// /// Number of elements in the queue.
|
||||
// /// </summary>
|
||||
// public int Count => _written;
|
||||
// /// <summary>
|
||||
// /// Collection containing data.
|
||||
// /// </summary>
|
||||
// private T[] Collection = new T[4];
|
||||
// /// <summary>
|
||||
// /// Current write index of the collection.
|
||||
// /// </summary>
|
||||
// public int WriteIndex { get; private set; }
|
||||
// /// <summary>
|
||||
// /// Buffer for resizing.
|
||||
// /// </summary>
|
||||
// private T[] _resizeBuffer = new T[0];
|
||||
// /// <summary>
|
||||
// /// Read position of the next Dequeue.
|
||||
// /// </summary>
|
||||
// private int _read;
|
||||
|
||||
// /// <summary>
|
||||
// /// Length of the queue.
|
||||
// /// </summary>
|
||||
// private int _written;
|
||||
|
||||
// /// <summary>
|
||||
// /// Enqueues an entry.
|
||||
// /// </summary>
|
||||
// /// <param name="data"></param>
|
||||
// public void Enqueue(T data)
|
||||
// {
|
||||
// if (_written == Collection.Length)
|
||||
// Resize();
|
||||
|
||||
// if (WriteIndex >= Collection.Length)
|
||||
// WriteIndex = 0;
|
||||
// Collection[WriteIndex] = data;
|
||||
|
||||
// WriteIndex++;
|
||||
// _written++;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Tries to dequeue the next entry.
|
||||
// /// </summary>
|
||||
// /// <param name="result">Dequeued entry.</param>
|
||||
// /// <returns>True if an entry existed to dequeue.</returns>
|
||||
// public bool TryDequeue(out T result)
|
||||
// {
|
||||
// if (_written == 0)
|
||||
// {
|
||||
// result = default;
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// result = Dequeue();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Dequeues the next entry.
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// public T Dequeue()
|
||||
// {
|
||||
// if (_written == 0)
|
||||
// throw new Exception($"Queue of type {typeof(T).Name} is empty.");
|
||||
|
||||
// T result = Collection[_read];
|
||||
|
||||
// _written--;
|
||||
// _read++;
|
||||
// if (_read >= Collection.Length)
|
||||
// _read = 0;
|
||||
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Tries to peek the next entry.
|
||||
// /// </summary>
|
||||
// /// <param name="result">Peeked entry.</param>
|
||||
// /// <returns>True if an entry existed to peek.</returns>
|
||||
// public bool TryPeek(out T result)
|
||||
// {
|
||||
// if (_written == 0)
|
||||
// {
|
||||
// result = default;
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// result = Peek();
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Peeks the next queue entry.
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// public T Peek()
|
||||
// {
|
||||
// if (_written == 0)
|
||||
// throw new Exception($"Queue of type {typeof(T).Name} is empty.");
|
||||
|
||||
// return Collection[_read];
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Clears the queue.
|
||||
// /// </summary>
|
||||
// public void Clear()
|
||||
// {
|
||||
// _read = 0;
|
||||
// WriteIndex = 0;
|
||||
// _written = 0;
|
||||
|
||||
// DefaultCollection(Collection);
|
||||
// DefaultCollection(_resizeBuffer);
|
||||
|
||||
// void DefaultCollection(T[] array)
|
||||
// {
|
||||
// int count = array.Length;
|
||||
// for (int i = 0; i < count; i++)
|
||||
// array[i] = default;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Doubles the queue size.
|
||||
// /// </summary>
|
||||
// private void Resize()
|
||||
// {
|
||||
// int length = _written;
|
||||
// int doubleLength = (length * 2);
|
||||
// int read = _read;
|
||||
|
||||
// /* Make sure copy array is the same size as current
|
||||
// * and copy contents into it. */
|
||||
// // Ensure large enough to fit contents.
|
||||
// T[] resizeBuffer = _resizeBuffer;
|
||||
// if (resizeBuffer.Length < doubleLength)
|
||||
// Array.Resize(ref resizeBuffer, doubleLength);
|
||||
// // Copy from the read of queue first.
|
||||
// int copyLength = (length - read);
|
||||
// Array.Copy(Collection, read, resizeBuffer, 0, copyLength);
|
||||
// /* If read index was higher than 0
|
||||
// * then copy remaining data as well from 0. */
|
||||
// if (read > 0)
|
||||
// Array.Copy(Collection, 0, resizeBuffer, copyLength, read);
|
||||
|
||||
// // Set _array to resize.
|
||||
// Collection = resizeBuffer;
|
||||
// // Reset positions.
|
||||
// _read = 0;
|
||||
// WriteIndex = length;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Returns value in actual index as it relates to simulated index.
|
||||
// /// </summary>
|
||||
// /// <param name="simulatedIndex">Simulated index to return. A value of 0 would return the first simulated index in the collection.</param>
|
||||
// /// <returns></returns>
|
||||
// public T this[int simulatedIndex]
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// int offset = GetRealIndex(simulatedIndex);
|
||||
// return Collection[offset];
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// int offset = GetRealIndex(simulatedIndex);
|
||||
// Collection[offset] = value;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Returns the real index of the collection using a simulated index.
|
||||
// /// </summary>
|
||||
// /// <param name="allowUnusedBuffer">True to allow an index be returned from an unused portion of the buffer so long as it is within bounds.</param>
|
||||
// private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false)
|
||||
// {
|
||||
// if (simulatedIndex >= Capacity)
|
||||
// {
|
||||
// return ReturnError();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// int written = _written;
|
||||
// // May be out of bounds if allowUnusedBuffer is false.
|
||||
// if (simulatedIndex >= written)
|
||||
// {
|
||||
// if (!allowUnusedBuffer)
|
||||
// return ReturnError();
|
||||
// }
|
||||
// int offset = (Capacity - written) + simulatedIndex + WriteIndex;
|
||||
// if (offset >= Capacity)
|
||||
// offset -= Capacity;
|
||||
|
||||
// return offset;
|
||||
// }
|
||||
|
||||
// int ReturnError()
|
||||
// {
|
||||
// UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Collection count is {_written}, Capacity is {Capacity}");
|
||||
// return -1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c0a46ad871780b45bfe317c8f904322
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FishNet.Utility.Performance
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves and stores byte arrays using a pooling system.
|
||||
/// </summary>
|
||||
public static class ByteArrayPool
|
||||
{
|
||||
/// <summary>
|
||||
/// Stored byte arrays.
|
||||
/// </summary>
|
||||
private static Queue<byte[]> _byteArrays = new();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a byte array which will be of at lesat minimum length. The returned array must manually be stored.
|
||||
/// </summary>
|
||||
public static byte[] Retrieve(int minimumLength)
|
||||
{
|
||||
byte[] result = null;
|
||||
|
||||
if (_byteArrays.Count > 0)
|
||||
result = _byteArrays.Dequeue();
|
||||
|
||||
int doubleMinimumLength = minimumLength * 2;
|
||||
if (result == null)
|
||||
result = new byte[doubleMinimumLength];
|
||||
else if (result.Length < minimumLength)
|
||||
Array.Resize(ref result, doubleMinimumLength);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a byte array for re-use.
|
||||
/// </summary>
|
||||
public static void Store(byte[] buffer)
|
||||
{
|
||||
/* Holy cow that's a lot of buffered
|
||||
* buffers. This wouldn't happen under normal
|
||||
* circumstances but if the user is stress
|
||||
* testing connections in one executable perhaps. */
|
||||
if (_byteArrays.Count > 300)
|
||||
return;
|
||||
_byteArrays.Enqueue(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7620a5e6fedc18408f8f04821b35bbd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,326 @@
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Object;
|
||||
using FishNet.Object;
|
||||
using FishNet.Utility.Extension;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Utility.Performance
|
||||
{
|
||||
public class DefaultObjectPool : ObjectPool
|
||||
{
|
||||
#region Public.
|
||||
/// <summary>
|
||||
/// Cache for pooled NetworkObjects.
|
||||
/// Key: CollectionId.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Dictionary<int, Stack<NetworkObject>>> Cache => _cache;
|
||||
private List<Dictionary<int, Stack<NetworkObject>>> _cache = new();
|
||||
#endregion
|
||||
|
||||
#region Serialized.
|
||||
/// <summary>
|
||||
/// True if to use object pooling.
|
||||
/// </summary>
|
||||
[Tooltip("True if to use object pooling.")]
|
||||
[SerializeField]
|
||||
private bool _enabled = true;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Current count of the cache collection.
|
||||
/// </summary>
|
||||
private int _cacheCount = 0;
|
||||
#endregion
|
||||
|
||||
#pragma warning disable CS0672 // Member overrides obsolete member
|
||||
public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, Transform parent = null, Vector3? nullablePosition = null, Quaternion? nullableRotation = null, Vector3? nullableScale = null, bool makeActive = true, bool asServer = true)
|
||||
#pragma warning restore CS0672 // Member overrides obsolete member
|
||||
{
|
||||
ObjectPoolRetrieveOption options = ObjectPoolRetrieveOption.Unset;
|
||||
if (makeActive)
|
||||
options |= ObjectPoolRetrieveOption.MakeActive;
|
||||
|
||||
return RetrieveObject(prefabId, collectionId, options, parent, nullablePosition, nullableRotation, nullableScale, asServer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
|
||||
/// </summary>
|
||||
/// <param name = "prefabId">PrefabId of the object to return.</param>
|
||||
/// <param name = "collectionId">CollectionId of the object to return.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, ObjectPoolRetrieveOption options, Transform parent = null, Vector3? nullablePosition = null, Quaternion? nullableRotation = null, Vector3? nullableScale = null, bool asServer = true)
|
||||
{
|
||||
bool makeActive = options.FastContains(ObjectPoolRetrieveOption.MakeActive);
|
||||
bool localSpace = options.FastContains(ObjectPoolRetrieveOption.LocalSpace);
|
||||
|
||||
if (!_enabled)
|
||||
return GetFromInstantiate();
|
||||
|
||||
Stack<NetworkObject> cache = GetCache(collectionId, prefabId, createIfMissing: true);
|
||||
NetworkObject nob = null;
|
||||
|
||||
// Iterate until nob is populated just in case cache entries have been destroyed.
|
||||
while (nob == null)
|
||||
{
|
||||
if (cache.TryPop(out nob))
|
||||
{
|
||||
if (nob != null)
|
||||
{
|
||||
nob.transform.SetParent(parent);
|
||||
if (localSpace)
|
||||
nob.transform.SetLocalPositionRotationAndScale(nullablePosition, nullableRotation, nullableScale);
|
||||
else
|
||||
nob.transform.SetWorldPositionRotationAndScale(nullablePosition, nullableRotation, nullableScale);
|
||||
|
||||
if (makeActive)
|
||||
nob.gameObject.SetActive(true);
|
||||
|
||||
return nob;
|
||||
}
|
||||
}
|
||||
// Nothing left in cache.
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall through, nothing in cache.
|
||||
return GetFromInstantiate();
|
||||
|
||||
// Returns a network object via instantation.
|
||||
NetworkObject GetFromInstantiate()
|
||||
{
|
||||
NetworkObject prefab = GetPrefab(prefabId, collectionId, asServer);
|
||||
if (prefab == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkObject result;
|
||||
Vector3 scale;
|
||||
|
||||
if (localSpace)
|
||||
{
|
||||
prefab.transform.OutLocalPropertyValues(nullablePosition, nullableRotation, nullableScale, out Vector3 pos, out Quaternion rot, out scale);
|
||||
if (parent != null)
|
||||
{
|
||||
// Convert pos and rot to world values for the instantiate.
|
||||
pos = parent.TransformPoint(pos);
|
||||
rot = parent.rotation * rot;
|
||||
}
|
||||
result = Instantiate(prefab, pos, rot, parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
prefab.transform.OutWorldPropertyValues(nullablePosition, nullableRotation, nullableScale, out Vector3 pos, out Quaternion rot, out scale);
|
||||
result = Instantiate(prefab, pos, rot, parent);
|
||||
}
|
||||
|
||||
result.transform.localScale = scale;
|
||||
|
||||
if (makeActive)
|
||||
result.gameObject.SetActive(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a prefab for prefab and collectionId.
|
||||
/// </summary>
|
||||
public override NetworkObject GetPrefab(int prefabId, ushort collectionId, bool asServer)
|
||||
{
|
||||
PrefabObjects po = NetworkManager.GetPrefabObjects<PrefabObjects>(collectionId, false);
|
||||
return po.GetObject(asServer, prefabId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an object into the pool.
|
||||
/// </summary>
|
||||
/// <param name = "instantiated">Object to store.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
public override void StoreObject(NetworkObject instantiated, bool asServer)
|
||||
{
|
||||
// Pooling is not enabled.
|
||||
if (!_enabled)
|
||||
{
|
||||
Destroy(instantiated.gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This call occurs here rather than in the object pool
|
||||
* to protect the user should they be using a
|
||||
* custom pool. */
|
||||
SetTransformToSerializedValues(instantiated);
|
||||
|
||||
// Get all children as well and reset state on them.
|
||||
List<NetworkObject> nestedNobs = instantiated.GetNetworkObjects(GetNetworkObjectOption.All);
|
||||
|
||||
foreach (NetworkObject nob in nestedNobs)
|
||||
nob.ResetState(asServer);
|
||||
|
||||
CollectionCaches<NetworkObject>.Store(nestedNobs);
|
||||
|
||||
// Set root inactive.
|
||||
instantiated.gameObject.SetActive(false);
|
||||
|
||||
Stack<NetworkObject> cache = GetCache(instantiated.SpawnableCollectionId, instantiated.PrefabId, createIfMissing: true);
|
||||
cache.Push(instantiated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a number of objects and adds them to the pool.
|
||||
/// </summary>
|
||||
/// <param name = "prefab">Prefab to cache.</param>
|
||||
/// <param name = "count">Quantity to spawn.</param>
|
||||
/// <param name = "asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
|
||||
#pragma warning disable CS0672 // Member overrides obsolete member
|
||||
public override void CacheObjects(NetworkObject prefab, int count, bool asServer) => StorePrefabObjects(prefab, count, asServer);
|
||||
#pragma warning restore CS0672 // Member overrides obsolete member
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a number of objects and adds them to the pool.
|
||||
/// </summary>
|
||||
/// <param name = "prefab">Prefab to cache.</param>
|
||||
/// <param name = "count">Quantity to spawn.</param>
|
||||
/// <param name = "asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
|
||||
/// <returns>Prefabs instantiated and added to cache.</returns>
|
||||
public override List<NetworkObject> StorePrefabObjects(NetworkObject prefab, int count, bool asServer)
|
||||
{
|
||||
if (!_enabled)
|
||||
return null;
|
||||
if (count <= 0)
|
||||
return null;
|
||||
if (prefab == null)
|
||||
return null;
|
||||
if (prefab.PrefabId == NetworkObject.UNSET_PREFABID_VALUE)
|
||||
{
|
||||
NetworkManager.LogError($"Pefab {prefab.name} has an invalid prefabId and cannot be cached.");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<NetworkObject> added = new();
|
||||
Stack<NetworkObject> cache = GetCache(prefab.SpawnableCollectionId, prefab.PrefabId, createIfMissing: true);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
NetworkObject nob = Instantiate(prefab);
|
||||
nob.gameObject.SetActive(false);
|
||||
cache.Push(nob);
|
||||
added.Add(nob);
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears pooled objects for a specific NetworkObject.
|
||||
/// </summary>
|
||||
/// <param name = "nob">Prefab or Instantiated NetworkObject to clear pool for.</param>
|
||||
/// <remarks>This will clear the entire pool for the specified object.</remarks>
|
||||
public void ClearPool(NetworkObject nob)
|
||||
{
|
||||
if (!_enabled)
|
||||
return;
|
||||
if (nob == null)
|
||||
return;
|
||||
|
||||
int spawnableCollectionId = nob.SpawnableCollectionId;
|
||||
Stack<NetworkObject> stack = GetCache(spawnableCollectionId, nob.PrefabId, createIfMissing: false);
|
||||
if (stack == null)
|
||||
return;
|
||||
|
||||
DestroyStackNetworkObjectsAndClear(stack);
|
||||
_cache[spawnableCollectionId].Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all pooled objects.
|
||||
/// </summary>
|
||||
public void ClearPool()
|
||||
{
|
||||
int count = _cache.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
ClearPool(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a pool destroying objects for a SpawnableCollectionId.
|
||||
/// </summary>
|
||||
/// <param name = "spawnableCollectionId">CollectionId to clear for.</param>
|
||||
public void ClearPool(int spawnableCollectionId)
|
||||
{
|
||||
if (spawnableCollectionId >= _cacheCount)
|
||||
return;
|
||||
|
||||
Dictionary<int, Stack<NetworkObject>> dict = _cache[spawnableCollectionId];
|
||||
|
||||
foreach (Stack<NetworkObject> item in dict.Values)
|
||||
DestroyStackNetworkObjectsAndClear(item);
|
||||
|
||||
dict.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cache for an id or creates one if does not exist.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Stack<NetworkObject> GetCache(int collectionId, int prefabId, bool createIfMissing)
|
||||
{
|
||||
if (collectionId >= _cacheCount)
|
||||
{
|
||||
// Do not create if missing.
|
||||
if (!createIfMissing)
|
||||
return null;
|
||||
|
||||
// Add more to the cache.
|
||||
while (_cache.Count <= collectionId)
|
||||
{
|
||||
Dictionary<int, Stack<NetworkObject>> dict = new();
|
||||
_cache.Add(dict);
|
||||
}
|
||||
_cacheCount = _cache.Count;
|
||||
}
|
||||
|
||||
Dictionary<int, Stack<NetworkObject>> dictionary = _cache[collectionId];
|
||||
// No cache for prefabId yet, make one.
|
||||
if (!dictionary.TryGetValueIL2CPP(prefabId, out Stack<NetworkObject> cache))
|
||||
{
|
||||
if (createIfMissing)
|
||||
{
|
||||
cache = new();
|
||||
dictionary[prefabId] = cache;
|
||||
}
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
[Obsolete("Use GetCache(int, int, bool)")]
|
||||
public Stack<NetworkObject> GetOrCreateCache(int collectionId, int prefabId) => GetCache(collectionId, prefabId, createIfMissing: true);
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all NetworkObjects within a stack and clears the stack.
|
||||
/// </summary>
|
||||
private void DestroyStackNetworkObjectsAndClear(Stack<NetworkObject> stack)
|
||||
{
|
||||
foreach (NetworkObject networkObject in stack)
|
||||
{
|
||||
if (networkObject != null)
|
||||
Destroy(networkObject.gameObject);
|
||||
}
|
||||
|
||||
stack.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb13d174096685549b1d6a94d726ff7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,96 @@
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Object;
|
||||
using FishNet.Object;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FishNet.Utility.Extension;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Utility.Performance
|
||||
{
|
||||
public abstract class ObjectPool : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// NetworkManager this ObjectPool belongs to.
|
||||
/// </summary>
|
||||
protected NetworkManager NetworkManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called at the end of every frame. This can be used to perform routine tasks.
|
||||
/// </summary>
|
||||
public virtual void LateUpdate() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this script for use.
|
||||
/// </summary>
|
||||
public virtual void InitializeOnce(NetworkManager nm)
|
||||
{
|
||||
NetworkManager = nm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
|
||||
/// </summary>
|
||||
/// <param name = "prefabId">PrefabId of the object to return.</param>
|
||||
/// <param name = "collectionId">CollectionId of the object to return.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use RetrieveObject(int, ushort, RetrieveOption, parent, Vector3?, Quaternion? Vector3?, bool) instead.")] // Remove in V5
|
||||
public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, Transform parent = null, Vector3? position = null, Quaternion? rotation = null, Vector3? scale = null, bool makeActive = true, bool asServer = true) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns an object that has been stored. A new object will be created if no stored objects are available.
|
||||
/// </summary>
|
||||
/// <param name = "prefabId">PrefabId of the object to return.</param>
|
||||
/// <param name = "collectionId">CollectionId of the object to return.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, ObjectPoolRetrieveOption options, Transform parent = null, Vector3? position = null, Quaternion? rotation = null, Vector3? scale = null, bool asServer = true) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a prefab using specified values.
|
||||
/// </summary>
|
||||
/// <param name = "prefabId">PrefabId of the object to return.</param>
|
||||
/// <param name = "collectionId">CollectionId of the object to return.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
public virtual NetworkObject GetPrefab(int prefabId, ushort collectionId, bool asServer)
|
||||
{
|
||||
PrefabObjects po = NetworkManager.GetPrefabObjects<PrefabObjects>(collectionId, false);
|
||||
return po.GetObject(asServer, prefabId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores an object into the pool.
|
||||
/// </summary>
|
||||
/// <param name = "instantiated">Object to store.</param>
|
||||
/// <param name = "asServer">True if being called on the server side.</param>
|
||||
/// <returns></returns>
|
||||
public abstract void StoreObject(NetworkObject instantiated, bool asServer);
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a number of objects and adds them to the pool.
|
||||
/// </summary>
|
||||
/// <param name = "prefab">Prefab to cache.</param>
|
||||
/// <param name = "count">Quantity to spawn.</param>
|
||||
/// <param name = "asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
|
||||
public virtual void CacheObjects(NetworkObject prefab, int count, bool asServer) { }
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a number of objects and adds them to the pool.
|
||||
/// </summary>
|
||||
/// <param name = "prefab">Prefab to cache.</param>
|
||||
/// <param name = "count">Quantity to spawn.</param>
|
||||
/// <param name = "asServer">True if storing prefabs for the server collection. This is only applicable when using DualPrefabObjects.</param>
|
||||
/// <returns>Prefabs instantiated and added to cache.</returns>
|
||||
public virtual List<NetworkObject> StorePrefabObjects(NetworkObject prefab, int count, bool asServer) => default;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the transform of a NetworkObject to it's values during serialization.
|
||||
/// </summary>
|
||||
public static void SetTransformToSerializedValues(NetworkObject nob)
|
||||
{
|
||||
nob.transform.SetLocalPositionRotationAndScale(nob.SerializedTransformProperties.Position, nob.SerializedTransformProperties.Rotation, nob.SerializedTransformProperties.Scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ec7d855ffa7afc45b619b84ddbda27c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FishNet.Utility.Performance
|
||||
{
|
||||
public static class RetrieveOptionExtensions
|
||||
{
|
||||
public static bool FastContains(this ObjectPoolRetrieveOption whole, ObjectPoolRetrieveOption part) => (whole & part) == part;
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum ObjectPoolRetrieveOption
|
||||
{
|
||||
Unset = 0,
|
||||
/// <summary>
|
||||
/// True to make the object active before returning.
|
||||
/// </summary>
|
||||
MakeActive = 1,
|
||||
/// <summary>
|
||||
/// True to treat supplied transform properties as local space.
|
||||
/// False will treat the properties as world space.
|
||||
/// </summary>
|
||||
LocalSpace = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30dbc4b626ddb7844aac0189cc4c7487
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,25 @@
|
||||
using FishNet.Object;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Utility.Performance
|
||||
{
|
||||
public static class GetNonAlloc
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all NetworkBehaviours on a transform.
|
||||
/// </summary>
|
||||
public static void GetNetworkBehavioursNonAlloc(this Transform t, ref List<NetworkBehaviour> results)
|
||||
{
|
||||
t.GetComponents(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all transforms on transform and it's children.
|
||||
/// </summary>
|
||||
public static void GetTransformsInChildrenNonAlloc(this Transform t, ref List<Transform> results, bool includeInactive = false)
|
||||
{
|
||||
t.GetComponentsInChildren(includeInactive, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d0740f919077254c8ffb131b9587407
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Performance/Transforms.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
|
||||
namespace FishNet.Component.Transforming
|
||||
{
|
||||
/// <summary>
|
||||
/// Axes to snap of properties.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum SnappedAxes : uint
|
||||
{
|
||||
Unset = 0,
|
||||
X = 1 << 0,
|
||||
Y = 1 << 1,
|
||||
Z = 1 << 2,
|
||||
Everything = Enums.SHIFT_EVERYTHING_UINT
|
||||
}
|
||||
|
||||
public static class SnappedAxesExtensions
|
||||
{
|
||||
public static bool FastContains(this SnappedAxes whole, SnappedAxes part) => (whole & part) == part;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0431c1ba9e0caa147aeccf016ceca606
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/SnappedAxes.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0c2d33d2e9aea44195c265bd08ff498
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using FishNet.Managing.Timing;
|
||||
using FishNet.Object;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Utility.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribes to tick events making them available as virtual methods.
|
||||
/// </summary>
|
||||
public abstract class TickNetworkBehaviour : NetworkBehaviour
|
||||
{
|
||||
#region Types.
|
||||
[System.Flags]
|
||||
[System.Serializable]
|
||||
public enum TickCallback : uint
|
||||
{
|
||||
None = 0,
|
||||
PreTick = 1 << 0,
|
||||
Tick = 1 << 1,
|
||||
PostTick = 1 << 2,
|
||||
Update = 1 << 3,
|
||||
LateUpdate = 1 << 4,
|
||||
Everything = Enums.SHIFT_EVERYTHING_UINT
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Tick callbacks to use.
|
||||
/// </summary>
|
||||
[Tooltip("Tick callbacks to use.")]
|
||||
[SerializeField]
|
||||
private TickCallback _tickCallbacks = TickCallback.Tick | TickCallback.PostTick;
|
||||
/// <summary>
|
||||
/// Last subscription state.
|
||||
/// </summary>
|
||||
private bool _subscribed;
|
||||
/// <summary>
|
||||
/// TimeManager subscribed to.
|
||||
/// </summary>
|
||||
private TimeManager _timeManager;
|
||||
|
||||
internal override void OnStartNetwork_Internal()
|
||||
{
|
||||
_timeManager = TimeManager;
|
||||
ChangeSubscriptions(subscribe: true);
|
||||
|
||||
base.OnStartNetwork_Internal();
|
||||
}
|
||||
|
||||
internal override void OnStopNetwork_Internal()
|
||||
{
|
||||
ChangeSubscriptions(subscribe: false);
|
||||
|
||||
base.OnStopNetwork_Internal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates callbacks to use and changes subscriptions accordingly.
|
||||
/// </summary>
|
||||
/// <param name = "value">Next value.</param>
|
||||
public void SetTickCallbacks(TickCallback value)
|
||||
{
|
||||
ChangeSubscriptions(subscribe: false);
|
||||
_tickCallbacks = value;
|
||||
if (value != TickCallback.None)
|
||||
ChangeSubscriptions(subscribe: true);
|
||||
}
|
||||
|
||||
private void ChangeSubscriptions(bool subscribe)
|
||||
{
|
||||
TimeManager tm = _timeManager;
|
||||
|
||||
if (tm == null)
|
||||
return;
|
||||
if (subscribe == _subscribed)
|
||||
return;
|
||||
_subscribed = subscribe;
|
||||
|
||||
if (subscribe)
|
||||
{
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.PreTick))
|
||||
tm.OnPreTick += TimeManager_OnPreTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.Tick))
|
||||
tm.OnTick += TimeManager_OnTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.PostTick))
|
||||
tm.OnPostTick += TimeManager_OnPostTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.Update))
|
||||
tm.OnUpdate += TimeManager_OnUpdate;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.LateUpdate))
|
||||
tm.OnLateUpdate += TimeManager_OnLateUpdate;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.PreTick))
|
||||
tm.OnPreTick -= TimeManager_OnPreTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.Tick))
|
||||
tm.OnTick -= TimeManager_OnTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.PostTick))
|
||||
tm.OnPostTick -= TimeManager_OnPostTick;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.Update))
|
||||
tm.OnUpdate -= TimeManager_OnUpdate;
|
||||
if (TickCallbackFastContains(_tickCallbacks, TickCallback.LateUpdate))
|
||||
tm.OnLateUpdate -= TimeManager_OnLateUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void TimeManager_OnPreTick() { }
|
||||
protected virtual void TimeManager_OnTick() { }
|
||||
protected virtual void TimeManager_OnPostTick() { }
|
||||
protected virtual void TimeManager_OnUpdate() { }
|
||||
protected virtual void TimeManager_OnLateUpdate() { }
|
||||
private bool TickCallbackFastContains(TickCallback whole, TickCallback part) => (whole & part) == part;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68553251b6087a342a59efd0b6433401
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,691 @@
|
||||
using System;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Timing;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Utility.Extension;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace FishNet.Component.Transforming
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is under regular development and it's API may change at any time.
|
||||
/// </summary>
|
||||
[Obsolete("This class will be removed in version 5.")]
|
||||
public sealed class TransformTickSmoother : IResettable
|
||||
{
|
||||
#region Types.
|
||||
private enum InitializeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Not initialized.
|
||||
/// </summary>
|
||||
Unset,
|
||||
/// <summary>
|
||||
/// Initialized for network use.
|
||||
/// </summary>
|
||||
Networked,
|
||||
/// <summary>
|
||||
/// Initialized for non-network use.
|
||||
/// </summary>
|
||||
NonNetworked
|
||||
}
|
||||
|
||||
[Preserve]
|
||||
private struct TickTransformProperties
|
||||
{
|
||||
public uint Tick;
|
||||
public TransformProperties Properties;
|
||||
|
||||
public TickTransformProperties(uint tick, Transform t)
|
||||
{
|
||||
Tick = tick;
|
||||
Properties = new(t.localPosition, t.localRotation, t.localScale);
|
||||
}
|
||||
|
||||
public TickTransformProperties(uint tick, Transform t, Vector3 localScale)
|
||||
{
|
||||
Tick = tick;
|
||||
Properties = new(t.localPosition, t.localRotation, localScale);
|
||||
}
|
||||
|
||||
public TickTransformProperties(uint tick, TransformProperties tp)
|
||||
{
|
||||
Tick = tick;
|
||||
Properties = tp;
|
||||
}
|
||||
|
||||
public TickTransformProperties(uint tick, TransformProperties tp, Vector3 localScale)
|
||||
{
|
||||
Tick = tick;
|
||||
tp.Scale = localScale;
|
||||
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.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.VeryLow;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the adaptiveInterpolation value.
|
||||
/// </summary>
|
||||
/// <param name = "adaptiveInterpolation">New value.</param>
|
||||
public void SetAdaptiveInterpolation(AdaptiveInterpolationType adaptiveInterpolation)
|
||||
{
|
||||
_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 _useOwnerSmoothing;
|
||||
/// <summary>
|
||||
/// True if Initialized has been called and settings have not been reset.
|
||||
/// </summary>
|
||||
private InitializeType _initializeType = InitializeType.Unset;
|
||||
/// <summary>
|
||||
/// Last tick this was teleported on.
|
||||
/// </summary>
|
||||
private uint _teleportedTick = TimeManager.UNSET_TICK;
|
||||
/// <summary>
|
||||
/// Last local tick a reconcile callback was received.
|
||||
/// </summary>
|
||||
private uint _lastReconcileTick = TimeManager.UNSET_TICK;
|
||||
/// <summary>
|
||||
/// Transform to track movement on.
|
||||
/// </summary>
|
||||
private Transform _rootTransform;
|
||||
/// <summary>
|
||||
/// Frame which smoothing can start.
|
||||
/// </summary>
|
||||
private int _startFrame;
|
||||
#endregion
|
||||
|
||||
#region Const.
|
||||
/// <summary>
|
||||
/// Default expected interval for reconciles.
|
||||
/// </summary>
|
||||
private const int RECONCILE_INTERVAL_DEFAULT = int.MaxValue;
|
||||
/// <summary>
|
||||
/// Maximum allowed entries to be queued over the interpolation amount.
|
||||
/// </summary>
|
||||
private const int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3;
|
||||
#endregion
|
||||
|
||||
[Preserve]
|
||||
public TransformTickSmoother() { }
|
||||
|
||||
~TransformTickSmoother()
|
||||
{
|
||||
// This is a last resort for if something didnt deinitialize right.
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this smoother for owner and spectator. This should only occur once.
|
||||
/// </summary>
|
||||
public void InitializeNetworked(NetworkObject nob, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, byte spectatorInterpolation, TransformPropertiesFlag specatorSmoothedProperties, AdaptiveInterpolationType adaptiveInterpolation)
|
||||
{
|
||||
ResetState();
|
||||
|
||||
_networkObject = nob;
|
||||
_spectatorInterpolation = spectatorInterpolation;
|
||||
_spectatorSmoothedProperties = specatorSmoothedProperties;
|
||||
|
||||
Initialize_Internal(nob.transform, graphicalObject, detach, teleportDistance, tickDelta, ownerInterpolation, ownerSmoothedProperties, forNetworked: true);
|
||||
|
||||
SetAdaptiveInterpolation(adaptiveInterpolation);
|
||||
UpdateInterpolation(0);
|
||||
}
|
||||
|
||||
private void Initialize_Internal(Transform rootTransform, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, bool forNetworked)
|
||||
{
|
||||
_rootTransform = rootTransform;
|
||||
_detach = detach;
|
||||
_transformProperties = CollectionCaches<TickTransformProperties>.RetrieveBasicQueue();
|
||||
_gfxInitializedOffsetValues = rootTransform.GetTransformOffsets(graphicalObject);
|
||||
_tickDelta = tickDelta;
|
||||
_graphicalObject = graphicalObject;
|
||||
_teleportThreshold = teleportDistance;
|
||||
_ownerInterpolation = ownerInterpolation;
|
||||
|
||||
_ownerSmoothedProperties = ownerSmoothedProperties;
|
||||
_initializeType = forNetworked ? InitializeType.Networked : InitializeType.NonNetworked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this smoother non-networked use.
|
||||
/// </summary>
|
||||
public void Initialize(Transform rootTransform, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties)
|
||||
{
|
||||
ResetState();
|
||||
|
||||
Initialize_Internal(rootTransform, graphicalObject, detach, teleportDistance, tickDelta, ownerInterpolation, ownerSmoothedProperties, false);
|
||||
|
||||
SetAdaptiveInterpolation(AdaptiveInterpolationType.Off);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deinitializes this smoother resetting values.
|
||||
/// </summary>
|
||||
public void Deinitialize()
|
||||
{
|
||||
ResetState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates interpolation based on localClient latency.
|
||||
/// </summary>
|
||||
private void UpdateInterpolation(uint clientStateTick)
|
||||
{
|
||||
/* Use owner interpolation if:
|
||||
* - Owner.
|
||||
* - Server, since server always interpolates over 1 tick; this only applies as clientHost.
|
||||
* - Not networked, use specified 'owner' interpolation.
|
||||
*/
|
||||
if (_initializeType == InitializeType.NonNetworked || _networkObject.IsServerInitialized || _networkObject.Owner.IsLocalClient)
|
||||
{
|
||||
_interpolation = _ownerInterpolation;
|
||||
}
|
||||
// Not using owner interpolation.
|
||||
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;
|
||||
}
|
||||
|
||||
interpolation *= GetInterpolationMultiplier();
|
||||
interpolation = Mathf.Clamp(interpolation, 2f, (float)byte.MaxValue);
|
||||
_interpolation = (byte)Mathf.CeilToInt(interpolation);
|
||||
|
||||
float GetInterpolationMultiplier()
|
||||
{
|
||||
switch (_adaptiveInterpolation)
|
||||
{
|
||||
case AdaptiveInterpolationType.ExtremelyLow:
|
||||
return 0.2f;
|
||||
case AdaptiveInterpolationType.VeryLow:
|
||||
return 0.45f;
|
||||
case AdaptiveInterpolationType.Low:
|
||||
return 0.8f;
|
||||
case AdaptiveInterpolationType.Moderate:
|
||||
return 1.05f;
|
||||
case AdaptiveInterpolationType.High:
|
||||
return 1.25f;
|
||||
case AdaptiveInterpolationType.VeryHigh:
|
||||
return 1.5f;
|
||||
// Make no changes for maximum.
|
||||
default:
|
||||
_networkObject.NetworkManager.LogError($"AdaptiveInterpolationType {_adaptiveInterpolation} is unhandled.");
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 OnUpdate()
|
||||
{
|
||||
if (!CanSmooth())
|
||||
return;
|
||||
|
||||
MoveToTarget(Time.deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the TimeManager invokes OnPreTick.
|
||||
/// </summary>
|
||||
public void OnPreTick()
|
||||
{
|
||||
if (!CanSmooth())
|
||||
return;
|
||||
|
||||
_preTicked = true;
|
||||
_useOwnerSmoothing = _networkObject == null || _networkObject.IsOwner;
|
||||
|
||||
DiscardExcessiveTransformPropertiesQueue();
|
||||
|
||||
//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()
|
||||
{
|
||||
if (!_networkObject.IsObjectReconciling)
|
||||
return;
|
||||
if (_networkObject.IsOwner || _adaptiveInterpolation == AdaptiveInterpolationType.Off)
|
||||
return;
|
||||
|
||||
uint clientStateTick = _networkObject.PredictionManager.ClientStateTick;
|
||||
_lastReconcileTick = clientStateTick;
|
||||
|
||||
UpdateInterpolation(clientStateTick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the TimeManager invokes OnPostReplay.
|
||||
/// </summary>
|
||||
/// <param name = "clientTick">Replay tick for the local client.</param>
|
||||
public void OnPostReplicateReplay(uint clientTick)
|
||||
{
|
||||
if (_networkObject.IsOwner || _adaptiveInterpolation == AdaptiveInterpolationType.Off)
|
||||
return;
|
||||
if (_transformProperties.Count == 0)
|
||||
return;
|
||||
if (clientTick <= _teleportedTick)
|
||||
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 (clientTick <= _teleportedTick)
|
||||
return;
|
||||
|
||||
//If preticked then previous transform values are known.
|
||||
if (_preTicked)
|
||||
{
|
||||
DiscardExcessiveTransformPropertiesQueue();
|
||||
|
||||
//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()
|
||||
{
|
||||
if (_networkObject == null)
|
||||
return;
|
||||
_teleportedTick = _networkObject.TimeManager.LocalTick;
|
||||
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.UNSET_VALUE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discards datas over interpolation limit from movement queue.
|
||||
/// </summary>
|
||||
private void DiscardExcessiveTransformPropertiesQueue()
|
||||
{
|
||||
int propertiesCount = _transformProperties.Count;
|
||||
int dequeueCount = propertiesCount - (_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();
|
||||
|
||||
SetMoveRates(tpp.Properties);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new transform properties and sets move rates if needed.
|
||||
/// </summary>
|
||||
private void AddTransformProperties(uint tick)
|
||||
{
|
||||
TickTransformProperties tpp = new(tick, GetNetworkObjectWorldPropertiesWithOffset());
|
||||
_transformProperties.Enqueue(tpp);
|
||||
|
||||
//If first entry then set move rates.
|
||||
if (_transformProperties.Count == 1)
|
||||
{
|
||||
TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties();
|
||||
SetMoveRates(gfxWorldProperties);
|
||||
_startFrame = Time.frameCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (tick != _transformProperties[index].Tick)
|
||||
{
|
||||
//Should not be possible.
|
||||
}
|
||||
else
|
||||
{
|
||||
_transformProperties[index] = new(tick, GetNetworkObjectWorldPropertiesWithOffset(), _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;
|
||||
if (_networkObject != null && _networkObject.EnablePrediction && !_networkObject.EnableStateForwarding && !_networkObject.IsController)
|
||||
return false;
|
||||
if (_networkObject.IsServerOnlyStarted)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets new rates based on next entries in transformProperties queue, against a supplied TransformProperties.
|
||||
/// </summary>
|
||||
private void SetMoveRates(in TransformProperties prevValues)
|
||||
{
|
||||
if (_transformProperties.Count == 0)
|
||||
{
|
||||
_moveRates = new(MoveRates.UNSET_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
TransformProperties nextValues = _transformProperties.Peek().Properties;
|
||||
float duration = _tickDelta;
|
||||
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 != 0)
|
||||
{
|
||||
_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 = 1f;
|
||||
}
|
||||
|
||||
_movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.95f, 1.05f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves transform to target values.
|
||||
/// </summary>
|
||||
private void MoveToTarget(float delta)
|
||||
{
|
||||
if (Time.frameCount < _startFrame)
|
||||
return;
|
||||
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 = _useOwnerSmoothing ? _ownerSmoothedProperties : _spectatorSmoothedProperties;
|
||||
_moveRates.Move(_graphicalObject, ttp.Properties, smoothedProperties, delta * _movementMultiplier, useWorldSpace: true);
|
||||
|
||||
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)
|
||||
{
|
||||
SetMoveRates(ttp.Properties);
|
||||
//If delta is negative then call move again with abs.
|
||||
if (tRemaining < 0f)
|
||||
MoveToTarget(Mathf.Abs(tRemaining));
|
||||
}
|
||||
//No remaining, set to snap.
|
||||
else
|
||||
{
|
||||
ClearTransformPropertiesQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetState()
|
||||
{
|
||||
if (_initializeType == InitializeType.Unset)
|
||||
return;
|
||||
|
||||
if (_graphicalObject != null)
|
||||
{
|
||||
if (_rootTransform != null)
|
||||
{
|
||||
//Check isQuitting for UnityEditor fix //https://github.com/FirstGearGames/FishNet/issues/818
|
||||
if (_detach && !ApplicationState.IsQuitting())
|
||||
_graphicalObject.SetParent(_rootTransform);
|
||||
_graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset());
|
||||
_graphicalObject = null;
|
||||
}
|
||||
else if (_detach)
|
||||
{
|
||||
UnityEngine.Object.Destroy(_graphicalObject.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_networkObject = null;
|
||||
_teleportedTick = TimeManager.UNSET_TICK;
|
||||
_lastReconcileTick = TimeManager.UNSET_TICK;
|
||||
_movementMultiplier = 1f;
|
||||
CollectionCaches<TickTransformProperties>.StoreAndDefault(ref _transformProperties);
|
||||
_teleportThreshold = default;
|
||||
_moveRates = default;
|
||||
_preTicked = default;
|
||||
_gfxInitializedOffsetValues = default;
|
||||
_gfxPreSimulateWorldValues = default;
|
||||
_tickDelta = default;
|
||||
_interpolation = default;
|
||||
}
|
||||
|
||||
public void InitializeState() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d265649320af034b9a4e966b0a341d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs
|
||||
uploadId: 866910
|
||||
Reference in New Issue
Block a user