using FishNet.Managing; using FishNet.Managing.Timing; using FishNet.Serializing; using FishNet.Transporting; using System; using System.Runtime.CompilerServices; using UnityEngine; namespace FishNet.Object.Synchronizing.Internal { public class SyncBase : ISyncType { #region Public. /// /// True if this SyncBase has been registered within it's containing class. /// public bool IsRegistered { get; private set; } /// /// True if the object for which this SyncType is for has been initialized for the network. /// public bool IsNetworkInitialized => (IsRegistered && (NetworkBehaviour.IsServer || NetworkBehaviour.IsClient)); /// /// True if a SyncObject, false if a SyncVar. /// public bool IsSyncObject { get; private set; } /// /// The settings for this SyncVar. /// public Settings Settings = new Settings(); /// /// How often updates may send. /// public float SendRate => Settings.SendRate; /// /// True if this SyncVar needs to send data. /// public bool IsDirty { get; private set; } /// /// NetworkManager this uses. /// public NetworkManager NetworkManager = null; /// /// NetworkBehaviour this SyncVar belongs to. /// public NetworkBehaviour NetworkBehaviour = null; /// /// Next time a SyncVar may send data/ /// public uint NextSyncTick = 0; /// /// Index within the sync collection. /// public uint SyncIndex { get; protected set; } = 0; /// /// Channel to send on. /// internal Channel Channel => _currentChannel; #endregion #region Private. /// /// Sync interval converted to ticks. /// private uint _timeToTicks; /// /// Channel to use for next write. To ensure eventual consistency this eventually changes to reliable when Settings are unreliable. /// private Channel _currentChannel; #endregion /// /// Initializes this SyncBase. /// public void InitializeInstance(NetworkBehaviour nb, uint syncIndex, WritePermission writePermissions, ReadPermission readPermissions, float tickRate, Channel channel, bool isSyncObject) { NetworkBehaviour = nb; SyncIndex = syncIndex; _currentChannel = channel; IsSyncObject = isSyncObject; Settings = new Settings() { WritePermission = writePermissions, ReadPermission = readPermissions, SendRate = tickRate, Channel = channel }; NetworkBehaviour.RegisterSyncType(this, SyncIndex); } /// /// Sets the SyncIndex. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetRegistered() { Registered(); } /// /// Called when the SyncType has been registered, but not yet initialized over the network. /// protected virtual void Registered() { IsRegistered = true; } /// /// PreInitializes this for use with the network. /// public void PreInitialize(NetworkManager networkManager) { NetworkManager = networkManager; if (Settings.SendRate < 0f) Settings.SendRate = networkManager.ServerManager.GetSynctypeRate(); _timeToTicks = NetworkManager.TimeManager.TimeToTicks(Settings.SendRate, TickRounding.RoundUp); } /// /// Called after OnStartXXXX has occurred. /// /// True if OnStartServer was called, false if OnStartClient. public virtual void OnStartCallback(bool asServer) { } protected bool CanNetworkSetValues(bool warn = true) { /* If not registered then values can be set * since at this point the object is still being initialized * in awake so we want those values to be applied. */ if (!IsRegistered) return true; /* If the network is not initialized yet then let * values be set. Values set here will not synchronize * to the network. We are assuming the user is setting * these values on client and server appropriately * since they are being applied prior to this object * being networked. */ if (!IsNetworkInitialized) return true; //If server is active then values can be set no matter what. if (NetworkBehaviour.IsServer) return true; //Predicted spawning is enabled. if (NetworkManager != null && NetworkManager.PredictionManager.GetAllowPredictedSpawning() && NetworkBehaviour.NetworkObject.AllowPredictedSpawning) return true; /* If here then server is not active and additional * checks must be performed. */ bool result = (Settings.ReadPermission == ReadPermission.ExcludeOwner && NetworkBehaviour.IsOwner); if (!result && warn) LogServerNotActiveWarning(); return result; } /// /// Logs that the operation could not be completed because the server is not active. /// protected void LogServerNotActiveWarning() { if (NetworkManager != null) NetworkManager.LogWarning($"Cannot complete operation as server when server is not active."); } /// /// Dirties this Sync and the NetworkBehaviour. /// public bool Dirty() { /* Reset channel even if already dirty. * This is because the value might have changed * which will reset the eventual consistency state. */ _currentChannel = Settings.Channel; /* Once dirty don't undirty until it's * processed. This ensures that data * is flushed. */ bool canDirty = NetworkBehaviour.DirtySyncType(IsSyncObject); IsDirty |= canDirty; return canDirty; } /// /// Sets IsDirty to false. /// internal void ResetDirty() { //If not a sync object and using unreliable channel. if (!IsSyncObject && Settings.Channel == Channel.Unreliable) { //Check if dirty can be unset or if another tick must be run using reliable. if (_currentChannel == Channel.Unreliable) _currentChannel = Channel.Reliable; //Already sent reliable, can undirty. Channel will reset next time this dirties. else IsDirty = false; } //If syncObject or using reliable unset dirty. else { IsDirty = false; } } /// /// True if dirty and enough time has passed to write changes. /// /// /// internal bool WriteTimeMet(uint tick) { return (IsDirty && tick >= NextSyncTick); } /// /// Writes current value. /// /// /// True to set the next time data may sync. [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void WriteDelta(PooledWriter writer, bool resetSyncTick = true) { WriteHeader(writer, resetSyncTick); } /// /// Writers the header for this SyncType. /// /// /// protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = true) { if (resetSyncTick) NextSyncTick = NetworkManager.TimeManager.Tick + _timeToTicks; writer.WriteByte((byte)SyncIndex); } /// /// Writes current value if not initialized value. /// /// public virtual void WriteFull(PooledWriter writer) { } /// /// Sets current value as client. /// /// [Obsolete("Use Read(PooledReader, bool).")] public virtual void Read(PooledReader reader) { } /// /// Sets current value as server or client. /// /// /// public virtual void Read(PooledReader reader, bool asServer) { } /// /// Resets to initialized values. /// public virtual void Reset() { NextSyncTick = 0; ResetDirty(); } } }