//This file contains values serialized in editor or once at runtime. using UnityEngine; using System; using GameKit.Dependencies.Utilities; using System.Collections.Generic; using FishNet.Managing; using FishNet.Utility.Extension; #if UNITY_EDITOR using UnityEditor.Experimental.SceneManagement; using UnityEditor.SceneManagement; using UnityEditor; #endif namespace FishNet.Object { public partial class NetworkObject : MonoBehaviour { #region Public. /// /// Networked PrefabId assigned to this Prefab. /// [field: SerializeField, HideInInspector] public ushort PrefabId { get; internal set; } = NetworkObject.UNSET_PREFABID_VALUE; /// /// Spawn collection to use assigned to this Prefab. /// [field: SerializeField, HideInInspector] public ushort SpawnableCollectionId { get; internal set; } = 0; /// /// Sets SceneId value. This is not synchronized automatically. /// /// public void SetSceneId(ulong sceneId) => SceneId = sceneId; /// /// Hash for the path which this asset resides. This value is set during edit time. /// [field: SerializeField, HideInInspector] public ulong AssetPathHash { get; private set; } /// /// Sets AssetPathhash value. /// /// Value to use. public void SetAssetPathHash(ulong value) => AssetPathHash = value; #endregion #region Internal. /// /// Network Id for this scene object. /// [field: SerializeField, HideInInspector] internal ulong SceneId { get; private set; } /// /// /// [SerializeField, HideInInspector] internal TransformProperties SerializedTransformProperties = new(); #endregion #region Private. /// /// Last time sceneIds were built automatically. /// [System.NonSerialized] private static double _lastSceneIdAutomaticRebuildTime; #endregion /// /// Removes SceneObject state. /// This may only be called at runtime. /// internal void ClearRuntimeSceneObject() { if (!Application.isPlaying) { NetworkManagerExtensions.LogError($"ClearRuntimeSceneObject may only be called at runtime."); return; } SceneId = NetworkObject.UNSET_SCENEID_VALUE; } #if UNITY_EDITOR private void OnApplicationQuit() { _lastSceneIdAutomaticRebuildTime = 0; } /// /// Tries to generate a SceneIds for NetworkObjects in a scene. /// internal static List CreateSceneId(UnityEngine.SceneManagement.Scene scene, bool force, out int changed) { changed = 0; if (Application.isPlaying) return new(); if (!scene.IsValid()) return new(); if (!scene.isLoaded) return new(); HashSet setIds = new(); uint scenePathHash = scene.path.GetStableHashU32(); List sceneNobs = new(); Scenes.GetSceneNetworkObjects(scene, firstOnly: false, errorOnDuplicates: false, ignoreUnsetSceneIds: false, ref sceneNobs); System.Random rnd = new(); //NetworkObjects which need their Ids rebuilt. List rebuildingNobs = new(); foreach (NetworkObject item in sceneNobs) { bool canGenerate = (!item.IsSceneObject || !setIds.Add(item.SceneId)); /* If an Id has not been generated yet or if it * already exist then rebuild for this object. */ if (force || canGenerate) { item.SceneId = NetworkObject.UNSET_SCENEID_VALUE; rebuildingNobs.Add(item); } } foreach (NetworkObject item in rebuildingNobs) { ulong nextSceneId = NetworkObject.UNSET_SCENEID_VALUE; while (nextSceneId == NetworkObject.UNSET_SCENEID_VALUE || setIds.Contains(nextSceneId)) { uint rndId = (uint)(rnd.Next(int.MinValue, int.MaxValue) + int.MaxValue); nextSceneId = CombineHashes(scenePathHash, rndId); } ulong CombineHashes(uint a, uint b) { return (b | a); } setIds.Add(nextSceneId); changed++; item.SceneId = nextSceneId; EditorUtility.SetDirty(item); } return sceneNobs; } /// /// Tries to generate a SceneId. /// private void CreateSceneId(bool force) { if (Application.isPlaying) return; //Unity bug, sometimes this can be null depending on editor callback orders. if (gameObject == null) return; //Not a scene object. if (string.IsNullOrEmpty(gameObject.scene.name)) { SceneId = NetworkObject.UNSET_SCENEID_VALUE; return; } /* If building then only check if * scene networkobjects have their sceneIds * missing. */ if (BuildPipeline.isBuildingPlayer) { //If prefab or part of a prefab, not a scene object. if (PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode() || //Not in a scene, another prefab check. !gameObject.scene.IsValid() || //Stored on disk, so is a prefab. Somehow prefabutility missed it. EditorUtility.IsPersistent(this)) //If here this is a sceneObject, but sceneId is not set. if (!IsSceneObject) throw new InvalidOperationException($"Networked GameObject {gameObject.name} in scene {gameObject.scene.path} is missing a SceneId. Use the Fish-Networking menu -> Utility -> Reserialize NetworkObjects > Reserialize Scenes. If the problem persist ensures {gameObject.name} does not have any missing script references on it's prefab or in the scene. Also ensure that you have any prefab changes for the object applied."); } //If not building check to rebuild sceneIds this for object and the scene its in. else { double realtime = EditorApplication.timeSinceStartup; //Only do this once every Xms to prevent excessive rebiulds. if (realtime - _lastSceneIdAutomaticRebuildTime < 0.250d) return; //Not in a scene, another prefab check. //Stored on disk, so is a prefab. Somehow prefabutility missed it. if (PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode() || !gameObject.scene.IsValid() || EditorUtility.IsPersistent(this)) return; _lastSceneIdAutomaticRebuildTime = realtime; CreateSceneId(gameObject.scene, force, out _); } } private bool IsEditingInPrefabMode() { if (EditorUtility.IsPersistent(this)) { // if the game object is stored on disk, it is a prefab of some kind, despite not returning true for IsPartOfPrefabAsset =/ return true; } else { // If the GameObject is not persistent let's determine which stage we are in first because getting Prefab info depends on it StageHandle mainStage = StageUtility.GetMainStageHandle(); StageHandle currentStage = StageUtility.GetStageHandle(gameObject); if (currentStage != mainStage) { var prefabStage = PrefabStageUtility.GetPrefabStage(gameObject); if (prefabStage != null) { return true; } } } return false; } private void ReferenceIds_Reset() { CreateSceneId(force: false); } #endif } }