265 lines
9.5 KiB
C#
265 lines
9.5 KiB
C#
|
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.
|
|||
|
/// <summary>
|
|||
|
/// True if this SyncBase has been registered within it's containing class.
|
|||
|
/// </summary>
|
|||
|
public bool IsRegistered { get; private set; }
|
|||
|
/// <summary>
|
|||
|
/// True if the object for which this SyncType is for has been initialized for the network.
|
|||
|
/// </summary>
|
|||
|
public bool IsNetworkInitialized => (IsRegistered && (NetworkBehaviour.IsServer || NetworkBehaviour.IsClient));
|
|||
|
/// <summary>
|
|||
|
/// True if a SyncObject, false if a SyncVar.
|
|||
|
/// </summary>
|
|||
|
public bool IsSyncObject { get; private set; }
|
|||
|
/// <summary>
|
|||
|
/// The settings for this SyncVar.
|
|||
|
/// </summary>
|
|||
|
public Settings Settings = new Settings();
|
|||
|
/// <summary>
|
|||
|
/// How often updates may send.
|
|||
|
/// </summary>
|
|||
|
public float SendRate => Settings.SendRate;
|
|||
|
/// <summary>
|
|||
|
/// True if this SyncVar needs to send data.
|
|||
|
/// </summary>
|
|||
|
public bool IsDirty { get; private set; }
|
|||
|
/// <summary>
|
|||
|
/// NetworkManager this uses.
|
|||
|
/// </summary>
|
|||
|
public NetworkManager NetworkManager = null;
|
|||
|
/// <summary>
|
|||
|
/// NetworkBehaviour this SyncVar belongs to.
|
|||
|
/// </summary>
|
|||
|
public NetworkBehaviour NetworkBehaviour = null;
|
|||
|
/// <summary>
|
|||
|
/// Next time a SyncVar may send data/
|
|||
|
/// </summary>
|
|||
|
public uint NextSyncTick = 0;
|
|||
|
/// <summary>
|
|||
|
/// Index within the sync collection.
|
|||
|
/// </summary>
|
|||
|
public uint SyncIndex { get; protected set; } = 0;
|
|||
|
/// <summary>
|
|||
|
/// Channel to send on.
|
|||
|
/// </summary>
|
|||
|
internal Channel Channel => _currentChannel;
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Private.
|
|||
|
/// <summary>
|
|||
|
/// Sync interval converted to ticks.
|
|||
|
/// </summary>
|
|||
|
private uint _timeToTicks;
|
|||
|
/// <summary>
|
|||
|
/// Channel to use for next write. To ensure eventual consistency this eventually changes to reliable when Settings are unreliable.
|
|||
|
/// </summary>
|
|||
|
private Channel _currentChannel;
|
|||
|
#endregion
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initializes this SyncBase.
|
|||
|
/// </summary>
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sets the SyncIndex.
|
|||
|
/// </summary>
|
|||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|||
|
public void SetRegistered()
|
|||
|
{
|
|||
|
Registered();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Called when the SyncType has been registered, but not yet initialized over the network.
|
|||
|
/// </summary>
|
|||
|
protected virtual void Registered()
|
|||
|
{
|
|||
|
IsRegistered = true;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// PreInitializes this for use with the network.
|
|||
|
/// </summary>
|
|||
|
public void PreInitialize(NetworkManager networkManager)
|
|||
|
{
|
|||
|
NetworkManager = networkManager;
|
|||
|
if (Settings.SendRate < 0f)
|
|||
|
Settings.SendRate = networkManager.ServerManager.GetSynctypeRate();
|
|||
|
|
|||
|
_timeToTicks = NetworkManager.TimeManager.TimeToTicks(Settings.SendRate, TickRounding.RoundUp);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Called after OnStartXXXX has occurred.
|
|||
|
/// </summary>
|
|||
|
/// <param name="asServer">True if OnStartServer was called, false if OnStartClient.</param>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Logs that the operation could not be completed because the server is not active.
|
|||
|
/// </summary>
|
|||
|
protected void LogServerNotActiveWarning()
|
|||
|
{
|
|||
|
if (NetworkManager != null)
|
|||
|
NetworkManager.LogWarning($"Cannot complete operation as server when server is not active.");
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Dirties this Sync and the NetworkBehaviour.
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sets IsDirty to false.
|
|||
|
/// </summary>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// True if dirty and enough time has passed to write changes.
|
|||
|
/// </summary>
|
|||
|
/// <param name="tick"></param>
|
|||
|
/// <returns></returns>
|
|||
|
internal bool WriteTimeMet(uint tick)
|
|||
|
{
|
|||
|
return (IsDirty && tick >= NextSyncTick);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Writes current value.
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
/// <param name="resetSyncTick">True to set the next time data may sync.</param>
|
|||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|||
|
public virtual void WriteDelta(PooledWriter writer, bool resetSyncTick = true)
|
|||
|
{
|
|||
|
WriteHeader(writer, resetSyncTick);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Writers the header for this SyncType.
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
/// <param name="resetSyncTick"></param>
|
|||
|
protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = true)
|
|||
|
{
|
|||
|
if (resetSyncTick)
|
|||
|
NextSyncTick = NetworkManager.TimeManager.Tick + _timeToTicks;
|
|||
|
|
|||
|
writer.WriteByte((byte)SyncIndex);
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// Writes current value if not initialized value.
|
|||
|
/// </summary>
|
|||
|
/// <param name="writer"></param>
|
|||
|
public virtual void WriteFull(PooledWriter writer) { }
|
|||
|
/// <summary>
|
|||
|
/// Sets current value as client.
|
|||
|
/// </summary>
|
|||
|
/// <param name="reader"></param>
|
|||
|
[Obsolete("Use Read(PooledReader, bool).")]
|
|||
|
public virtual void Read(PooledReader reader) { }
|
|||
|
/// <summary>
|
|||
|
/// Sets current value as server or client.
|
|||
|
/// </summary>
|
|||
|
/// <param name="reader"></param>
|
|||
|
/// <param name="asServer"></param>
|
|||
|
public virtual void Read(PooledReader reader, bool asServer) { }
|
|||
|
/// <summary>
|
|||
|
/// Resets to initialized values.
|
|||
|
/// </summary>
|
|||
|
public virtual void Reset()
|
|||
|
{
|
|||
|
NextSyncTick = 0;
|
|||
|
ResetDirty();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|