using FishNet.CodeGenerating; using FishNet.Documenting; using FishNet.Managing.Transporting; using FishNet.Serializing.Helping; using FishNet.Utility; using System.Runtime.CompilerServices; using FishNet.Managing; using UnityEngine; [assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] namespace FishNet.Object { /// /// Scripts which inherit from NetworkBehaviour can be used to gain insight of, and perform actions on the network. /// [ExcludeSerialization] public abstract partial class NetworkBehaviour : MonoBehaviour { #region Public. /// /// True if this NetworkBehaviour is initialized for the network. /// public bool IsSpawned => _networkObjectCache.IsSpawned; /// /// /// [SerializeField, HideInInspector] private byte _componentIndexCache = NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID; /// /// ComponentIndex for this NetworkBehaviour. /// public byte ComponentIndex { get => _componentIndexCache; private set => _componentIndexCache = value; } #if UNITY_EDITOR /// /// NetworkObject automatically added or discovered during edit time. /// [SerializeField, HideInInspector] private NetworkObject _addedNetworkObject; #endif /// /// Cache of the TransportManager. /// private TransportManager _transportManagerCache; /// /// /// [SerializeField, HideInInspector] private NetworkObject _networkObjectCache; /// /// NetworkObject this behaviour is for. /// public NetworkObject NetworkObject => _networkObjectCache; #endregion #region Private. /// /// True if initialized at some point asServer. /// private bool _initializedOnceServer; #pragma warning disable CS0414 /// /// True if initialized at some point not asServer. /// private bool _initializedOnceClient; #pragma warning restore CS0414 #endregion #region Consts. /// /// Maximum number of allowed added NetworkBehaviours. /// public const byte MAXIMUM_NETWORKBEHAVIOURS = (UNSET_NETWORKBEHAVIOUR_ID - 1); /// /// Id for when a NetworkBehaviour is not valid. /// public const byte UNSET_NETWORKBEHAVIOUR_ID = byte.MaxValue; #endregion /// /// Outputs data about this NetworkBehaviour to string. /// /// public override string ToString() { return $"Name [{gameObject.name}] ComponentId [{ComponentIndex}] NetworkObject Name [{_networkObjectCache.name}] NetworkObject Id [{_networkObjectCache.ObjectId}]"; } [MakePublic] internal virtual void NetworkInitialize___Early() { } [MakePublic] internal virtual void NetworkInitialize___Late() { } /// /// Preinitializes this script for the network. /// internal void InitializeEarly(NetworkObject nob, bool asServer) { _transportManagerCache = nob.TransportManager; SyncTypes_Preinitialize(asServer); if (asServer) { InitializeRpcLinks(); _initializedOnceServer = true; } else { if (!_initializedOnceClient && nob.EnablePrediction && _usesPrediction) nob.RegisterPredictionBehaviourOnce(this); _initializedOnceClient = true; } } internal void Deinitialize(bool asServer) { ResetState_SyncTypes(asServer); } /// /// Called by the NetworkObject when this object is destroyed. /// internal void NetworkBehaviour_OnDestroy() { SyncTypes_OnDestroy(); } /// /// Serializes information for network components. /// internal void SerializeComponents(NetworkObject nob, byte componentIndex) { _networkObjectCache = nob; ComponentIndex = componentIndex; } /// /// Manually initializes network content for the NetworkBehaviour if the object it's on is disabled. /// internal void InitializeIfDisabled() { if (gameObject.activeInHierarchy) return; NetworkInitializeIfDisabled(); } /// /// Long name is to prevent users from potentially creating their own method named the same. /// [MakePublic] [APIExclude] internal virtual void NetworkInitializeIfDisabled() { } #region Editor. protected virtual void Reset() { #if UNITY_EDITOR if (Application.isPlaying) return; TryAddNetworkObject(); #endif } protected virtual void OnValidate() { #if UNITY_EDITOR if (Application.isPlaying) return; TryAddNetworkObject(); #endif } /// /// Resets this NetworkBehaviour so that it may be added to an object pool. /// public virtual void ResetState(bool asServer) { ResetState_SyncTypes(asServer); ResetState_Prediction(asServer); ClearReplicateCache(); ClearBuffedRpcs(); } /// /// Tries to add the NetworkObject component. /// private NetworkObject TryAddNetworkObject() { #if UNITY_EDITOR if (Application.isPlaying) return _addedNetworkObject; if (_addedNetworkObject != null) { AlertToDuplicateNetworkObjects(_addedNetworkObject.transform); return _addedNetworkObject; } /* Manually iterate up the chain because GetComponentInParent doesn't * work when modifying prefabs in the inspector. Unity, you're starting * to suck a lot right now. */ NetworkObject result = null; Transform climb = transform; while (climb != null) { if (climb.TryGetComponent(out result)) break; else climb = climb.parent; } if (result != null) { _addedNetworkObject = result; } //Not found, add a new nob. else { _addedNetworkObject = transform.root.gameObject.AddComponent(); NetworkManagerExtensions.Log($"Script {GetType().Name} on object {gameObject.name} added a NetworkObject component to {transform.root.name}."); } AlertToDuplicateNetworkObjects(_addedNetworkObject.transform); return _addedNetworkObject; //Removes duplicate network objects from t. void AlertToDuplicateNetworkObjects(Transform t) { NetworkObject[] nobs = t.GetComponents(); //This shouldn't be possible but does occur sometimes; maybe a unity bug? if (nobs.Length > 1) { //Update added to first entryt. _addedNetworkObject = nobs[0]; string useMenu = " You may also use the Fish-Networking menu to automatically remove duplicate NetworkObjects."; string sceneName = t.gameObject.scene.name; if (string.IsNullOrEmpty(sceneName)) Debug.LogError($"Prefab {t.name} has multiple NetworkObject components. Please remove the extra component(s) to prevent errors.{useMenu}"); else Debug.LogError($"Object {t.name} in scene {sceneName} has multiple NetworkObject components. Please remove the extra component(s) to prevent errors.{useMenu}"); } } #else return null; #endif } #endregion } }