using FishNet.Managing.Predicting; using FishNet.Managing.Timing; using FishNet.Object; using GameKit.Dependencies.Utilities; using UnityEngine; namespace FishNet.Component.Transforming.Beta { /// /// Smoothes this object between ticks. /// /// This can be configured to smooth over a set interval of time, or to smooth adaptively and make path corrections for prediction. public class TickSmootherController : IResettable { #region Public. /// /// Logic for owner smoothing. /// public UniversalTickSmoother UniversalSmoother { get; private set; } #endregion #region Private. /// /// /// private InitializationSettings _initializationSettings = new(); /// /// /// private MovementSettings _ownerMovementSettings = new(); /// /// /// private MovementSettings _spectatorMovementSettings = new(); /// /// True if OnDestroy has been called. /// private bool _destroyed; /// /// Cached timeManager reference. /// private TimeManager _timeManager; /// /// NetworkBehaviour which initialized this object. Value may be null when initialized for an Offline smoother. /// private NetworkBehaviour _initializingNetworkBehaviour; /// /// Transform which initialized this object. /// private Transform _graphicalTransform; /// /// True if initialized with a null NetworkBehaviour. /// private bool _initializedOffline; /// /// True if subscribed to events used for adaptiveInterpolation. /// private bool _subscribedToAdaptiveEvents; /// /// True if currently subscribed to events. /// private bool _subscribed; /// /// True if initialized. /// private bool _isInitialized; #endregion public void Initialize(InitializationSettings initializationSettings, MovementSettings ownerSettings, MovementSettings spectatorSettings) { _initializingNetworkBehaviour = initializationSettings.InitializingNetworkBehaviour; _graphicalTransform = initializationSettings.GraphicalTransform; _initializationSettings = initializationSettings; _ownerMovementSettings = ownerSettings; _spectatorMovementSettings = spectatorSettings; _initializedOffline = (initializationSettings.InitializingNetworkBehaviour == null); _isInitialized = true; } public void OnDestroy() { ChangeSubscriptions(false); StoreSmoother(); _destroyed = true; _isInitialized = false; } public void StartSmoother() { if (!_isInitialized) return; bool canStart = (_initializedOffline) ? StartOffline() : StartOnline(); if (!canStart) return; RetrieveSmoothers(); UniversalSmoother.Initialize(_initializationSettings, _ownerMovementSettings, _spectatorMovementSettings); UniversalSmoother.StartSmoother(); bool StartOnline() { NetworkBehaviour nb = _initializingNetworkBehaviour; SetTimeManager(nb.TimeManager); return true; } bool StartOffline() { if (_timeManager == null) return false; return true; } } public void StopSmoother() { ChangeSubscriptions(subscribe: false); if (!_initializedOffline) StopOnline(); if (UniversalSmoother != null) UniversalSmoother.StopSmoother(); void StopOnline() { SetTimeManager(tm: null); } //Intentionally left blank. //void StopOffline() { } } public void TimeManager_OnUpdate() { UniversalSmoother.OnUpdate(Time.deltaTime); } public void TimeManager_OnPreTick() { UniversalSmoother.OnPreTick(); } /// /// Called after a tick completes. /// public void TimeManager_OnPostTick() { if (_timeManager != null) UniversalSmoother.OnPostTick(_timeManager.LocalTick); } private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick) { UniversalSmoother.OnPostReplicateReplay(clientTick); } private void TimeManager_OnRoundTripTimeUpdated(long rttMs) { UniversalSmoother.UpdateRealtimeInterpolation(); } /// /// Stores smoothers if they have value. /// private void StoreSmoother() { if (UniversalSmoother == null) return; ResettableObjectCaches.Store(UniversalSmoother); UniversalSmoother = null; } /// /// Stores current smoothers and retrieves new ones. /// private void RetrieveSmoothers() { StoreSmoother(); UniversalSmoother = ResettableObjectCaches.Retrieve(); } // /// // /// Sets a target transform to follow. // /// // public void SetTargetTransform(Transform value) // { // Transform currentTargetTransform = _initializationSettings.TargetTransform; // // if (value == currentTargetTransform) // return; // // bool clientStartCalled = (_initializedOffline && _timeManager != null) || (_initializingNetworkBehaviour != null && _initializingNetworkBehaviour.OnStartClientCalled); // // bool previousTargetTransformIsValid = (currentTargetTransform != null); // // //If target is different and old is not null then reset. // if (previousTargetTransformIsValid && clientStartCalled) // OnStopClient(); // // _initializationSettings.TargetTransform = value; // if (previousTargetTransformIsValid && clientStartCalled) // OnStartClient(); // } /// /// Sets a new PredictionManager to use. /// public void SetTimeManager(TimeManager tm) { if (tm == _timeManager) return; //Unsub from current. ChangeSubscriptions(false); //Sub to newest. _timeManager = tm; ChangeSubscriptions(true); } /// /// Changes the subscription to the TimeManager. /// private void ChangeSubscriptions(bool subscribe) { if (_destroyed) return; TimeManager tm = _timeManager; if (tm == null) return; if (subscribe == _subscribed) return; _subscribed = subscribe; bool adaptiveIsOff = (_ownerMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off && _spectatorMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off); if (subscribe) { tm.OnUpdate += TimeManager_OnUpdate; tm.OnPreTick += TimeManager_OnPreTick; tm.OnPostTick += TimeManager_OnPostTick; if (!adaptiveIsOff) { tm.OnRoundTripTimeUpdated += TimeManager_OnRoundTripTimeUpdated; PredictionManager pm = tm.NetworkManager.PredictionManager; pm.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; _subscribedToAdaptiveEvents = true; } } else { tm.OnUpdate -= TimeManager_OnUpdate; tm.OnPreTick -= TimeManager_OnPreTick; tm.OnPostTick -= TimeManager_OnPostTick; if (_subscribedToAdaptiveEvents) { tm.OnRoundTripTimeUpdated -= TimeManager_OnRoundTripTimeUpdated; PredictionManager pm = tm.NetworkManager.PredictionManager; pm.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; } } } public void ResetState() { _initializationSettings = default; _ownerMovementSettings = default; _spectatorMovementSettings = default; _destroyed = false; _timeManager = null; _initializingNetworkBehaviour = null; _graphicalTransform = null; _subscribed = false; _subscribedToAdaptiveEvents = false; _isInitialized = false; } public void InitializeState() { } } }