863 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			863 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.Managing;
 | |
| using FishNet.Connection;
 | |
| using UnityEngine;
 | |
| using FishNet.Serializing;
 | |
| using FishNet.Transporting;
 | |
| using System.Collections.Generic;
 | |
| using System.Runtime.CompilerServices;
 | |
| using FishNet.Utility.Performance;
 | |
| using System;
 | |
| using FishNet.Managing.Object;
 | |
| using FishNet.Component.Ownership;
 | |
| #if UNITY_EDITOR
 | |
| using UnityEditor;
 | |
| #endif
 | |
| 
 | |
| namespace FishNet.Object
 | |
| {
 | |
|     [DisallowMultipleComponent]
 | |
|     public sealed partial class NetworkObject : MonoBehaviour
 | |
|     {
 | |
|         #region Public.
 | |
|         /// <summary>
 | |
|         /// True if was nested during scene setup or within a prefab.
 | |
|         /// </summary>
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         public bool IsNested { get; private set; }
 | |
|         /// <summary>
 | |
|         /// NetworkConnection which predicted spawned this object.
 | |
|         /// </summary>
 | |
|         public NetworkConnection PredictedSpawner { get; private set; } = NetworkManager.EmptyConnection;
 | |
|         /// <summary>
 | |
|         /// True if this NetworkObject was active during edit. Will be true if placed in scene during edit, and was in active state on run.
 | |
|         /// </summary>
 | |
|         [System.NonSerialized]
 | |
|         internal bool ActiveDuringEdit;
 | |
|         /// <summary>
 | |
|         /// Returns if this object was placed in the scene during edit-time.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         public bool IsSceneObject => (SceneId > 0);
 | |
|         /// <summary>
 | |
|         /// ComponentIndex for this NetworkBehaviour.
 | |
|         /// </summary>
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         public byte ComponentIndex { get; private set; }
 | |
|         /// <summary>
 | |
|         /// Unique Id for this NetworkObject. This does not represent the object owner.
 | |
|         /// </summary>
 | |
|         public int ObjectId { get; private set; }
 | |
|         /// <summary>
 | |
|         /// True if this NetworkObject is deinitializing. Will also be true until Initialize is called. May be false until the object is cleaned up if object is destroyed without using Despawn.
 | |
|         /// </summary>
 | |
|         internal bool IsDeinitializing { get; private set; } = true;
 | |
|         /// <summary>
 | |
|         /// PredictedSpawn component on this object. Will be null if not added manually.
 | |
|         /// </summary>
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         public PredictedSpawn PredictedSpawn { get; private set; }
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         private NetworkBehaviour[] _networkBehaviours;
 | |
|         /// <summary>
 | |
|         /// NetworkBehaviours within the root and children of this object.
 | |
|         /// </summary>        
 | |
|         public NetworkBehaviour[] NetworkBehaviours
 | |
|         {
 | |
|             get => _networkBehaviours;
 | |
|             private set => _networkBehaviours = value;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// NetworkObject parenting this instance. The parent NetworkObject will be null if there was no parent during serialization.
 | |
|         /// </summary>
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         public NetworkObject ParentNetworkObject { get; private set; }
 | |
|         /// <summary>
 | |
|         /// NetworkObjects nested beneath this one. Recursive NetworkObjects may exist within each entry of this field.
 | |
|         /// </summary> 
 | |
|         [field: SerializeField, HideInInspector]
 | |
|         public List<NetworkObject> ChildNetworkObjects { get; private set; } = new List<NetworkObject>();
 | |
|         /// <summary>
 | |
|         /// 
 | |
|         /// </summary>
 | |
|         [SerializeField, HideInInspector]
 | |
|         internal TransformProperties SerializedTransformProperties = new TransformProperties();
 | |
|         /// <summary>
 | |
|         /// Current state of the NetworkObject.
 | |
|         /// </summary>
 | |
|         [System.NonSerialized]
 | |
|         internal NetworkObjectState State = NetworkObjectState.Unset;
 | |
|         #endregion
 | |
| 
 | |
|         #region Serialized.
 | |
|         /// <summary>
 | |
|         /// True if the object will always initialize as a networked object. When false the object will not automatically initialize over the network. Using Spawn() on an object will always set that instance as networked.
 | |
|         /// </summary>
 | |
|         public bool IsNetworked
 | |
|         {
 | |
|             get => _isNetworked;
 | |
|             private set => _isNetworked = value;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Sets IsNetworked value. This method must be called before Start.
 | |
|         /// </summary>
 | |
|         /// <param name="value">New IsNetworked value.</param>
 | |
|         public void SetIsNetworked(bool value)
 | |
|         {
 | |
|             IsNetworked = value;
 | |
|         }
 | |
|         [Tooltip("True if the object will always initialize as a networked object. When false the object will not automatically initialize over the network. Using Spawn() on an object will always set that instance as networked.")]
 | |
|         [SerializeField]
 | |
|         private bool _isNetworked = true;
 | |
|         /// <summary>
 | |
|         /// True to make this object global, and added to the DontDestroyOnLoad scene. This value may only be set for instantiated objects, and can be changed if done immediately after instantiating.
 | |
|         /// </summary>
 | |
|         public bool IsGlobal
 | |
|         {
 | |
|             get => _isGlobal;
 | |
|             private set => _isGlobal = value;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Sets IsGlobal value.
 | |
|         /// </summary>
 | |
|         /// <param name="value">New global value.</param>
 | |
|         public void SetIsGlobal(bool value)
 | |
|         {
 | |
|             if (IsNested)
 | |
|             {
 | |
|                 NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot change IsGlobal because it is nested. Only root objects may be set global.");
 | |
|                 return;
 | |
|             }
 | |
|             if (!IsDeinitializing)
 | |
|             {
 | |
|                 NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot change IsGlobal as it's already initialized. IsGlobal may only be changed immediately after instantiating.");
 | |
|                 return;
 | |
|             }
 | |
|             if (IsSceneObject)
 | |
|             {
 | |
|                 NetworkManager.StaticLogWarning($"Object {gameObject.name} cannot have be global because it is a scene object. Only instantiated objects may be global.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             _networkObserverInitiliazed = false;
 | |
|             IsGlobal = value;
 | |
|         }
 | |
|         [Tooltip("True to make this object global, and added to the DontDestroyOnLoad scene. This value may only be set for instantiated objects, and can be changed if done immediately after instantiating.")]
 | |
|         [SerializeField]
 | |
|         private bool _isGlobal;
 | |
|         /// <summary>
 | |
|         /// Order to initialize this object's callbacks when spawned with other NetworkObjects in the same tick. Default value is 0, negative values will execute callbacks first.
 | |
|         /// </summary>
 | |
|         public sbyte GetInitializeOrder() => _initializeOrder;
 | |
|         [Tooltip("Order to initialize this object's callbacks when spawned with other NetworkObjects in the same tick. Default value is 0, negative values will execute callbacks first.")]
 | |
|         [SerializeField]
 | |
|         private sbyte _initializeOrder = 0;
 | |
|         /// <summary>
 | |
|         /// How to handle this object when it despawns. Scene objects are never destroyed when despawning.
 | |
|         /// </summary>
 | |
|         [SerializeField]
 | |
|         [Tooltip("How to handle this object when it despawns. Scene objects are never destroyed when despawning.")]
 | |
|         private DespawnType _defaultDespawnType = DespawnType.Destroy;
 | |
|         /// <summary>
 | |
|         /// True to use configured ObjectPool rather than destroy this NetworkObject when being despawned. Scene objects are never destroyed.
 | |
|         /// </summary>
 | |
|         public DespawnType GetDefaultDespawnType() => _defaultDespawnType;
 | |
|         /// <summary>
 | |
|         /// Sets DespawnType value.
 | |
|         /// </summary>
 | |
|         /// <param name="despawnType">Default despawn type for this NetworkObject.</param>
 | |
|         public void SetDefaultDespawnType(DespawnType despawnType)
 | |
|         {
 | |
|             _defaultDespawnType = despawnType;
 | |
|         }
 | |
|         #endregion
 | |
| 
 | |
|         #region Private.
 | |
|         /// <summary>
 | |
|         /// True if disabled NetworkBehaviours have been initialized.
 | |
|         /// </summary>
 | |
|         private bool _disabledNetworkBehavioursInitialized;
 | |
|         #endregion
 | |
| 
 | |
|         #region Const.
 | |
|         /// <summary>
 | |
|         /// Value used when the ObjectId has not been set.
 | |
|         /// </summary>
 | |
|         public const int UNSET_OBJECTID_VALUE = ushort.MaxValue;
 | |
|         /// <summary>
 | |
|         /// Value used when the PrefabId has not been set.
 | |
|         /// </summary>
 | |
|         public const int UNSET_PREFABID_VALUE = ushort.MaxValue;
 | |
|         #endregion
 | |
| 
 | |
|         #region Editor Debug.
 | |
| #if UNITY_EDITOR
 | |
|         private int _editorOwnerId;
 | |
| #endif
 | |
|         #endregion
 | |
| 
 | |
|         private void Awake()
 | |
|         {
 | |
|             SetChildDespawnedState();
 | |
|         }
 | |
| 
 | |
|         private void Start()
 | |
|         {
 | |
|             TryStartDeactivation();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes NetworkBehaviours if they are disabled.
 | |
|         /// </summary>
 | |
|         private void InitializeNetworkBehavioursIfDisabled()
 | |
|         {
 | |
|             if (_disabledNetworkBehavioursInitialized)
 | |
|                 return;
 | |
|             _disabledNetworkBehavioursInitialized = true;
 | |
| 
 | |
|             for (int i = 0; i < NetworkBehaviours.Length; i++)
 | |
|                 NetworkBehaviours[i].InitializeIfDisabled();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets Despawned on child NetworkObjects if they are not enabled.
 | |
|         /// </summary>
 | |
|         private void SetChildDespawnedState()
 | |
|         {
 | |
|             NetworkObject nob;
 | |
|             for (int i = 0; i < ChildNetworkObjects.Count; i++)
 | |
|             {
 | |
|                 nob = ChildNetworkObjects[i];
 | |
|                 if (!nob.gameObject.activeSelf)
 | |
|                     nob.State = NetworkObjectState.Despawned;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Deactivates this NetworkObject during it's start cycle if conditions are met.
 | |
|         /// </summary>
 | |
|         internal void TryStartDeactivation()
 | |
|         {
 | |
|             if (!IsNetworked)
 | |
|                 return;
 | |
| 
 | |
|             //Global.
 | |
|             if (IsGlobal && !IsSceneObject)
 | |
|                 DontDestroyOnLoad(gameObject);
 | |
| 
 | |
|             if (NetworkManager == null || (!NetworkManager.IsClient && !NetworkManager.IsServer))
 | |
|             {
 | |
|                 //ActiveDuringEdit is only used for scene objects.
 | |
|                 if (IsSceneObject)
 | |
|                     ActiveDuringEdit = true;
 | |
|                 gameObject.SetActive(false);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void OnDisable()
 | |
|         {
 | |
|             /* If deinitializing and an owner exist
 | |
|              * then remove object from owner. */
 | |
|             if (IsDeinitializing && Owner.IsValid)
 | |
|                 Owner.RemoveObject(this);
 | |
|             /* If not nested then check to despawn this OnDisable.
 | |
|              * A nob may become disabled without being despawned if it's
 | |
|              * beneath another deinitializing nob. This can be true even while
 | |
|              * not nested because users may move a nob under another at runtime.
 | |
|              * 
 | |
|              * This object must also be activeSelf, meaning that it became disabled
 | |
|              * because a parent was. If not activeSelf then it's possible the
 | |
|              * user simply deactivated the object themselves. */
 | |
|             else if (IsServer && !IsNested && gameObject.activeSelf)
 | |
|             {
 | |
|                 bool canDespawn = false;
 | |
|                 Transform nextParent = transform.parent;
 | |
|                 while (nextParent != null)
 | |
|                 {
 | |
|                     if (nextParent.TryGetComponent(out NetworkObject pNob))
 | |
|                     {
 | |
|                         /* If pNob is not the same as ParentNetworkObject
 | |
|                          * then that means this object was moved around. It could be
 | |
|                          * that this was previously a child of something else
 | |
|                          * or that was given a parent later on in it's life cycle.
 | |
|                          ^
 | |
|                          * When this occurs do not send a despawn for this object.
 | |
|                          * Rather, let it destroy from unity callbacks which will force
 | |
|                         * the proper destroy/stop cycle. */
 | |
|                         if (pNob != ParentNetworkObject)
 | |
|                             break;
 | |
|                         //If nob is deinitialized then this one cannot exist.
 | |
|                         if (pNob.IsDeinitializing)
 | |
|                         {
 | |
|                             canDespawn = true;
 | |
|                             break;
 | |
|                         }
 | |
|                     }
 | |
|                     nextParent = nextParent.parent;
 | |
|                 }
 | |
| 
 | |
|                 if (canDespawn)
 | |
|                     Despawn();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void OnDestroy()
 | |
|         {
 | |
|             //Does this need to be here? I'm thinking no, remove it and examine later. //todo
 | |
|             if (Owner.IsValid)
 | |
|                 Owner.RemoveObject(this);
 | |
|             //Already being deinitialized by FishNet.
 | |
|             if (IsDeinitializing)
 | |
|                 return;
 | |
| 
 | |
|             if (NetworkManager != null)
 | |
|             {
 | |
|                 //Was destroyed without going through the proper methods.
 | |
|                 if (NetworkManager.IsServer)
 | |
|                     NetworkManager.ServerManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, true);
 | |
|                 if (NetworkManager.IsClient)
 | |
|                     NetworkManager.ClientManager.Objects.NetworkObjectUnexpectedlyDestroyed(this, false);
 | |
|             }
 | |
| 
 | |
|             /* When destroyed unexpectedly it's
 | |
|              * impossible to know if this occurred on
 | |
|              * the server or client side, so send callbacks
 | |
|              * for both. */
 | |
|             if (IsServer)
 | |
|                 InvokeStopCallbacks(true);
 | |
|             if (IsClient)
 | |
|                 InvokeStopCallbacks(false);
 | |
| 
 | |
|             /* If owner exist then remove object from owner.
 | |
|              * This has to be called here as well OnDisable because
 | |
|              * the OnDisable will only remove the object if
 | |
|              * deinitializing. This is because the object shouldn't
 | |
|              * be removed from owner if the object is simply being
 | |
|              * disabled, but not deinitialized. But in the scenario
 | |
|              * the object is unexpectedly destroyed, which is how we
 | |
|              * arrive here, the object needs to be removed from owner. */
 | |
|             if (Owner.IsValid)
 | |
|                 Owner.RemoveObject(this);
 | |
| 
 | |
|             Observers.Clear();
 | |
|             IsDeinitializing = true;
 | |
| 
 | |
|             SetActiveStatus(false);
 | |
|             //Do not need to set state if being destroyed.
 | |
|             //Don't need to reset sync types if object is being destroyed.
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets IsClient or IsServer to isActive.
 | |
|         /// </summary>
 | |
|         private void SetActiveStatus(bool isActive, bool server)
 | |
|         {
 | |
|             if (server)
 | |
|                 IsServer = isActive;
 | |
|             else
 | |
|                 IsClient = isActive;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Sets IsClient and IsServer to isActive.
 | |
|         /// </summary>
 | |
|         private void SetActiveStatus(bool isActive)
 | |
|         {
 | |
|             IsServer = isActive;
 | |
|             IsClient = isActive;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Initializes this script. This is only called once even when as host.
 | |
|         /// </summary>
 | |
|         /// <param name="networkManager"></param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void Preinitialize_Internal(NetworkManager networkManager, int objectId, NetworkConnection owner, bool asServer)
 | |
|         {
 | |
|             State = NetworkObjectState.Spawned;
 | |
|             InitializeNetworkBehavioursIfDisabled();
 | |
|             IsDeinitializing = false;
 | |
|             //QOL references.
 | |
|             NetworkManager = networkManager;
 | |
|             ServerManager = networkManager.ServerManager;
 | |
|             ClientManager = networkManager.ClientManager;
 | |
|             ObserverManager = networkManager.ObserverManager;
 | |
|             TransportManager = networkManager.TransportManager;
 | |
|             TimeManager = networkManager.TimeManager;
 | |
|             SceneManager = networkManager.SceneManager;
 | |
|             PredictionManager = networkManager.PredictionManager;
 | |
|             RollbackManager = networkManager.RollbackManager;
 | |
| 
 | |
|             SetOwner(owner);
 | |
|             ObjectId = objectId;
 | |
| 
 | |
|             /* This must be called at the beginning
 | |
|              * so that all conditions are handled by the observer
 | |
|              * manager prior to the preinitialize call on networkobserver. 
 | |
|              * The method called is dependent on NetworkManager being set. */
 | |
|             AddDefaultNetworkObserverConditions();
 | |
| 
 | |
|             for (int i = 0; i < NetworkBehaviours.Length; i++)
 | |
|                 NetworkBehaviours[i].InitializeOnce_Internal();
 | |
| 
 | |
|             /* NetworkObserver uses some information from
 | |
|              * NetworkBehaviour so it must be preinitialized
 | |
|              * after NetworkBehaviours are. */
 | |
|             if (asServer)
 | |
|                 NetworkObserver.PreInitialize(this);
 | |
|             _networkObserverInitiliazed = true;
 | |
| 
 | |
|             //Add to connection objects if owner exist.
 | |
|             if (owner != null)
 | |
|                 owner.AddObject(this);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Adds a NetworkBehaviour and serializes it's components.
 | |
|         /// </summary>
 | |
|         internal T AddAndSerialize<T>() where T : NetworkBehaviour //runtimeNB make public.
 | |
|         {
 | |
|             int startingLength = NetworkBehaviours.Length;
 | |
|             T result = gameObject.AddComponent<T>();
 | |
|             //Add to network behaviours.
 | |
|             Array.Resize(ref _networkBehaviours, startingLength + 1);
 | |
|             _networkBehaviours[startingLength] = result;
 | |
|             //Serialize values and return.
 | |
|             result.SerializeComponents(this, (byte)startingLength);
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Updates NetworkBehaviours and initializes them with serialized values.
 | |
|         /// </summary>
 | |
|         /// <param name="fromPrefabCollection">True if this call originated from a prefab collection, such as during it's initialization.</param>
 | |
|         internal void UpdateNetworkBehaviours(NetworkObject parentNob, ref byte componentIndex) //runtimeNB make public.
 | |
|         {
 | |
|             /* This method can be called by the developer initializing prefabs, the prefab collection doing it automatically,
 | |
|              * or when the networkobject is modified or added to an object.
 | |
|              * 
 | |
|              * Prefab collections generally contain all prefabs, meaning they will not only call this on the topmost
 | |
|              * networkobject but also each child, as the child would be it's own prefab in the collection. This assumes
 | |
|              * that is, the child is a nested prefab.
 | |
|              * 
 | |
|              * Because of this potential a check must be done where if the componentIndex is 0 we must look
 | |
|              * for a networkobject above this one. If there is a networkObject above this one then we know the prefab
 | |
|              * is being initialized individually, not part of a recursive check. In this case exit early
 | |
|              * as the parent would have already resolved the needed information. */
 | |
| 
 | |
|             //If first componentIndex make sure there's no more than maximum allowed nested nobs.
 | |
|             if (componentIndex == 0)
 | |
|             {
 | |
|                 //Not possible for index to be 0 and nested.
 | |
|                 if (IsNested)
 | |
|                     return;
 | |
|                 byte maxNobs = 255;
 | |
|                 if (GetComponentsInChildren<NetworkObject>(true).Length > maxNobs)
 | |
|                 {
 | |
|                     Debug.LogError($"The number of child NetworkObjects on {gameObject.name} exceeds the maximum of {maxNobs}.");
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             PredictedSpawn = GetComponent<PredictedSpawn>();
 | |
|             ComponentIndex = componentIndex;
 | |
|             ParentNetworkObject = parentNob;
 | |
| 
 | |
|             //Transforms which can be searched for networkbehaviours.
 | |
|             ListCache<Transform> transformCache = ListCaches.GetTransformCache();
 | |
|             transformCache.Reset();
 | |
|             ChildNetworkObjects.Clear();
 | |
| 
 | |
|             transformCache.AddValue(transform);
 | |
|             for (int z = 0; z < transformCache.Written; z++)
 | |
|             {
 | |
|                 Transform currentT = transformCache.Collection[z];
 | |
|                 for (int i = 0; i < currentT.childCount; i++)
 | |
|                 {
 | |
|                     Transform t = currentT.GetChild(i);
 | |
|                     /* If contains a nob then do not add to transformsCache.
 | |
|                      * Do add to ChildNetworkObjects so it can be initialized when
 | |
|                      * parent is. */
 | |
|                     if (t.TryGetComponent(out NetworkObject childNob))
 | |
|                     {
 | |
|                         /* Make sure both objects have the same value for
 | |
|                          * IsSceneObject. It's possible the user instantiated
 | |
|                          * an object and placed it beneath a scene object
 | |
|                          * before the scene initialized. They may also
 | |
|                          * add a scene object under an instantiated, even though
 | |
|                          * this almost certainly will break things. */
 | |
|                         if (IsSceneObject == childNob.IsSceneObject)
 | |
|                             ChildNetworkObjects.Add(childNob);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         transformCache.AddValue(t);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             int written;
 | |
|             //Iterate all cached transforms and get networkbehaviours.
 | |
|             ListCache<NetworkBehaviour> nbCache = ListCaches.GetNetworkBehaviourCache();
 | |
|             nbCache.Reset();
 | |
|             written = transformCache.Written;
 | |
|             List<Transform> ts = transformCache.Collection;
 | |
|             //
 | |
|             for (int i = 0; i < written; i++)
 | |
|                 nbCache.AddValues(ts[i].GetNetworkBehaviours());
 | |
| 
 | |
|             //Copy to array.
 | |
|             written = nbCache.Written;
 | |
|             List<NetworkBehaviour> nbs = nbCache.Collection;
 | |
|             NetworkBehaviours = new NetworkBehaviour[written];
 | |
|             //
 | |
|             for (int i = 0; i < written; i++)
 | |
|             {
 | |
|                 NetworkBehaviours[i] = nbs[i];
 | |
|                 NetworkBehaviours[i].SerializeComponents(this, (byte)i);
 | |
|             }
 | |
| 
 | |
|             ListCaches.StoreCache(transformCache);
 | |
|             ListCaches.StoreCache(nbCache);
 | |
| 
 | |
|             //Tell children nobs to update their NetworkBehaviours.
 | |
|             foreach (NetworkObject item in ChildNetworkObjects)
 | |
|             {
 | |
|                 componentIndex++;
 | |
|                 item.UpdateNetworkBehaviours(this, ref componentIndex);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Called after all data is synchronized with this NetworkObject.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void Initialize(bool asServer, bool invokeSyncTypeCallbacks)
 | |
|         {
 | |
|             InitializeCallbacks(asServer, invokeSyncTypeCallbacks);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Called to prepare this object to be destroyed or disabled.
 | |
|         /// </summary>
 | |
|         internal void Deinitialize(bool asServer)
 | |
|         {
 | |
|             InvokeStopCallbacks(asServer);
 | |
|             if (asServer)
 | |
|             {
 | |
|                 IsDeinitializing = true;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 ClientManager.Connection.LevelOfDetails.Remove(this);
 | |
|                 //Client only.
 | |
|                 if (!NetworkManager.IsServer)
 | |
|                     IsDeinitializing = true;
 | |
| 
 | |
|                 RemoveClientRpcLinkIndexes();
 | |
|             }
 | |
| 
 | |
|             SetActiveStatus(false, asServer);
 | |
|             if (asServer)
 | |
|                 Observers.Clear();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Resets states for object to be pooled.
 | |
|         /// </summary>
 | |
|         /// <param name="asServer">True if performing as server.</param>
 | |
|         public void ResetForObjectPool()
 | |
|         {
 | |
|             int count = NetworkBehaviours.Length;
 | |
|             for (int i = 0; i < count; i++)
 | |
|                 NetworkBehaviours[i].ResetForObjectPool();
 | |
| 
 | |
|             State = NetworkObjectState.Unset;
 | |
|             SetOwner(NetworkManager.EmptyConnection);
 | |
|             NetworkObserver.Deinitialize();
 | |
|             //QOL references.
 | |
|             NetworkManager = null;
 | |
|             ServerManager = null;
 | |
|             ClientManager = null;
 | |
|             ObserverManager = null;
 | |
|             TransportManager = null;
 | |
|             TimeManager = null;
 | |
|             SceneManager = null;
 | |
|             RollbackManager = null;
 | |
|             //Misc sets.
 | |
|             ObjectId = 0;
 | |
|             ClientInitialized = false;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Removes ownership from all clients.
 | |
|         /// </summary>
 | |
|         public void RemoveOwnership()
 | |
|         {
 | |
|             GiveOwnership(null, true);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gives ownership to newOwner.
 | |
|         /// </summary>
 | |
|         /// <param name="newOwner"></param>
 | |
|         public void GiveOwnership(NetworkConnection newOwner)
 | |
|         {
 | |
|             GiveOwnership(newOwner, true);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gives ownership to newOwner.
 | |
|         /// </summary>
 | |
|         /// <param name="newOwner"></param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         internal void GiveOwnership(NetworkConnection newOwner, bool asServer)
 | |
|         {
 | |
|             /* Additional asServer checks. */
 | |
|             if (asServer)
 | |
|             {
 | |
|                 if (!NetworkManager.IsServer)
 | |
|                 {
 | |
|                     NetworkManager.LogWarning($"Ownership cannot be given for object {gameObject.name}. Only server may give ownership.");
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 //If the same owner don't bother sending a message, just ignore request.
 | |
|                 if (newOwner == Owner && asServer)
 | |
|                     return;
 | |
| 
 | |
|                 if (newOwner != null && newOwner.IsActive && !newOwner.LoadedStartScenes(true))
 | |
|                 {
 | |
|                     NetworkManager.LogWarning($"Ownership has been transfered to ConnectionId {newOwner.ClientId} but this is not recommended until after they have loaded start scenes. You can be notified when a connection loads start scenes by using connection.OnLoadedStartScenes on the connection, or SceneManager.OnClientLoadStartScenes.");
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             bool activeNewOwner = (newOwner != null && newOwner.IsActive);
 | |
| 
 | |
|             //Set prevOwner, disallowing null.
 | |
|             NetworkConnection prevOwner = Owner;
 | |
|             if (prevOwner == null)
 | |
|                 prevOwner = NetworkManager.EmptyConnection;
 | |
| 
 | |
|             SetOwner(newOwner);
 | |
|             /* Only modify objects if asServer or not
 | |
|              * host. When host, server would
 | |
|              * have already modified objects
 | |
|              * collection so there is no need
 | |
|              * for client to as well. */
 | |
|             if (asServer || !NetworkManager.IsHost)
 | |
|             {
 | |
|                 if (activeNewOwner)
 | |
|                     newOwner.AddObject(this);
 | |
|                 if (prevOwner.IsValid && prevOwner != newOwner)
 | |
|                     prevOwner.RemoveObject(this);
 | |
|             }
 | |
| 
 | |
|             //After changing owners invoke callbacks.
 | |
|             InvokeOwnership(prevOwner, asServer);
 | |
| 
 | |
|             //If asServer send updates to clients as needed.
 | |
|             if (asServer)
 | |
|             {
 | |
|                 if (activeNewOwner)
 | |
|                     ServerManager.Objects.RebuildObservers(this, newOwner);
 | |
| 
 | |
|                 using (PooledWriter writer = WriterPool.GetWriter())
 | |
|                 {
 | |
|                     writer.WritePacketId(PacketId.OwnershipChange);
 | |
|                     writer.WriteNetworkObject(this);
 | |
|                     writer.WriteNetworkConnection(Owner);
 | |
|                     //If sharing then send to all observers.
 | |
|                     if (NetworkManager.ServerManager.ShareIds)
 | |
|                     {
 | |
|                         NetworkManager.TransportManager.SendToClients((byte)Channel.Reliable, writer.GetArraySegment(), this);
 | |
|                     }
 | |
|                     //Only sending to old / new.
 | |
|                     else
 | |
|                     {
 | |
|                         if (prevOwner.IsActive)
 | |
|                             NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), prevOwner);
 | |
|                         if (activeNewOwner)
 | |
|                             NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), newOwner);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (prevOwner.IsActive)
 | |
|                     ServerManager.Objects.RebuildObservers(prevOwner);
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes a predicted object for client.
 | |
|         /// </summary>
 | |
|         internal void InitializePredictedObject_Server(NetworkManager manager, NetworkConnection predictedSpawner)
 | |
|         {
 | |
|             NetworkManager = manager;
 | |
|             PredictedSpawner = predictedSpawner;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Initializes a predicted object for client.
 | |
|         /// </summary>
 | |
|         internal void PreinitializePredictedObject_Client(NetworkManager manager, int objectId, NetworkConnection owner, NetworkConnection predictedSpawner)
 | |
|         {
 | |
|             PredictedSpawner = predictedSpawner;
 | |
|             Preinitialize_Internal(manager, objectId, owner, false);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Deinitializes this predicted spawned object.
 | |
|         /// </summary>
 | |
|         internal void DeinitializePredictedObject_Client()
 | |
|         {
 | |
|             /* For the time being we're just going to disable the object because
 | |
|              * deinitializing instead could present a lot of problems.
 | |
|              * For example: if client deinitializes rpc links are unregistered,
 | |
|              * and if server had a rpc on the way already the link would
 | |
|              * not be found. This would cause the reader length to be wrong
 | |
|              * resulting in packet corruption. */
 | |
|             gameObject.SetActive(false);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets the owner of this object.
 | |
|         /// </summary>
 | |
|         /// <param name="owner"></param>
 | |
|         /// <param name="allowNull"></param>
 | |
|         private void SetOwner(NetworkConnection owner)
 | |
|         {
 | |
|             Owner = owner;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if this NetworkObject is a scene object, and has changed.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         internal ChangedTransformProperties GetTransformChanges(TransformProperties stp)
 | |
|         {
 | |
|             ChangedTransformProperties ctp = ChangedTransformProperties.Unset;
 | |
|             if (transform.localPosition != stp.Position)
 | |
|                 ctp |= ChangedTransformProperties.LocalPosition;
 | |
|             if (transform.localRotation != stp.Rotation)
 | |
|                 ctp |= ChangedTransformProperties.LocalRotation;
 | |
|             if (transform.localScale != stp.LocalScale)
 | |
|                 ctp |= ChangedTransformProperties.LocalScale;
 | |
| 
 | |
|             return ctp;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns if this NetworkObject is a scene object, and has changed.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         internal ChangedTransformProperties GetTransformChanges(GameObject prefab)
 | |
|         {
 | |
|             Transform t = prefab.transform;
 | |
|             ChangedTransformProperties ctp = ChangedTransformProperties.Unset;
 | |
|             if (transform.position != t.position)
 | |
|                 ctp |= ChangedTransformProperties.LocalPosition;
 | |
|             if (transform.rotation != t.rotation)
 | |
|                 ctp |= ChangedTransformProperties.LocalRotation;
 | |
|             if (transform.localScale != t.localScale)
 | |
|                 ctp |= ChangedTransformProperties.LocalScale;
 | |
| 
 | |
|             return ctp;
 | |
|         }
 | |
| 
 | |
|         #region Editor.
 | |
| #if UNITY_EDITOR
 | |
|         /// <summary>
 | |
|         /// Removes duplicate NetworkObject components on this object returning the removed count.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         internal int RemoveDuplicateNetworkObjects()
 | |
|         {
 | |
|             NetworkObject[] nobs = GetComponents<NetworkObject>();
 | |
|             for (int i = 1; i < nobs.Length; i++)
 | |
|                 DestroyImmediate(nobs[i]);
 | |
| 
 | |
|             return (nobs.Length - 1);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Sets IsNested and returns the result.
 | |
|         /// </summary>
 | |
|         /// <returns></returns>
 | |
|         private bool SetIsNestedThroughTraversal()
 | |
|         {
 | |
|             Transform parent = transform.parent;
 | |
|             //Iterate long as parent isn't null, and isnt self.
 | |
|             while (parent != null && parent != transform)
 | |
|             {
 | |
|                 if (parent.TryGetComponent<NetworkObject>(out _))
 | |
|                 {
 | |
|                     IsNested = true;
 | |
|                     return IsNested;
 | |
|                 }
 | |
| 
 | |
|                 parent = parent.parent;
 | |
|             }
 | |
| 
 | |
|             //No NetworkObject found in parents, meaning this is not nested.
 | |
|             IsNested = false;
 | |
|             return IsNested;
 | |
|         }
 | |
| 
 | |
|         private void OnValidate()
 | |
|         {
 | |
|             SetIsNestedThroughTraversal();
 | |
|             SceneUpdateNetworkBehaviours();
 | |
|             ReferenceIds_OnValidate();
 | |
| 
 | |
|             if (IsGlobal && IsSceneObject)
 | |
|                 Debug.LogWarning($"Object {gameObject.name} will have it's IsGlobal state ignored because it is a scene object. Instantiated copies will still be global. This warning is informative only.");
 | |
|         }
 | |
| 
 | |
|         private void Reset()
 | |
|         {
 | |
|             SetIsNestedThroughTraversal();
 | |
|             SerializeTransformProperties();
 | |
|             SceneUpdateNetworkBehaviours();
 | |
|             ReferenceIds_Reset();
 | |
|         }
 | |
| 
 | |
|         private void SceneUpdateNetworkBehaviours()
 | |
|         {
 | |
|             //In a scene.
 | |
|             if (!string.IsNullOrEmpty(gameObject.scene.name))
 | |
|             {
 | |
|                 if (IsNested)
 | |
|                     return;
 | |
| 
 | |
|                 byte componentIndex = 0;
 | |
|                 UpdateNetworkBehaviours(null, ref componentIndex);
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         private void OnDrawGizmosSelected()
 | |
|         {
 | |
|             _editorOwnerId = (Owner == null) ? -1 : Owner.ClientId;
 | |
|             SerializeTransformProperties();
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Serializes TransformProperties to current transform properties.
 | |
|         /// </summary>
 | |
|         private void SerializeTransformProperties()
 | |
|         {
 | |
|             /* Use this method to set scene data since it doesn't need to exist outside 
 | |
|             * the editor and because its updated regularly while selected. */
 | |
|             //If a scene object.
 | |
|             if (!EditorApplication.isPlaying && !string.IsNullOrEmpty(gameObject.scene.name))
 | |
|             {
 | |
|                 SerializedTransformProperties = new TransformProperties(
 | |
|                     transform.localPosition, transform.localRotation, transform.localScale);
 | |
|             }
 | |
|         }
 | |
| #endif
 | |
|         #endregion
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 |