This repository has been archived on 2023-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
station_obscurum_unity/Assets/FishNet/Runtime/Object/NetworkObject.ReferenceIds.cs

214 lines
8.2 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using System;
using FishNet.Object.Helping;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor.Experimental.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor;
#endif
namespace FishNet.Object
{
public sealed partial class NetworkObject : MonoBehaviour
{
#region Serialized.
/// <summary>
/// Networked PrefabId assigned to this Prefab.
/// </summary>
[field: SerializeField, HideInInspector]
public ushort PrefabId { get; internal set; } = 0;
/// <summary>
/// Spawn collection to use assigned to this Prefab.
/// </summary>
[field: SerializeField, HideInInspector]
public ushort SpawnableCollectionId { get; internal set; } = 0;
#pragma warning disable 414 //Disabled because Unity thinks tihs is unused when building.
/// <summary>
/// Hash to the scene which this object resides.
/// </summary>
[SerializeField, HideInInspector]
private uint _scenePathHash;
#pragma warning restore 414
/// <summary>
/// Network Id for this scene object.
/// </summary>
[field: SerializeField, HideInInspector]
internal ulong SceneId { get; private set; }
/// <summary>
/// Hash for the path which this asset resides. This value is set during edit time.
/// </summary>
[field: SerializeField, HideInInspector]
public ulong AssetPathHash { get; private set; }
/// <summary>
/// Sets AssetPathhash value.
/// </summary>
/// <param name="value">Value to use.</param>
public void SetAssetPathHash(ulong value) => AssetPathHash = value;
#endregion
#if UNITY_EDITOR
/// <summary>
/// This is used to store NetworkObjects in the scene during edit time.
/// SceneIds are compared against this collection to ensure there are no duplicated.
/// </summary>
[SerializeField, HideInInspector]
private List<NetworkObject> _sceneNetworkObjects = new List<NetworkObject>();
#endif
/// <summary>
/// Removes SceneObject state.
/// This may only be called at runtime.
/// </summary>
internal void ClearRuntimeSceneObject()
{
if (!Application.isPlaying)
{
Debug.LogError($"ClearRuntimeSceneObject may only be called at runtime.");
return;
}
SceneId = 0;
}
#if UNITY_EDITOR
/// <summary>
/// Tries to generate a SceneId.
/// </summary>
internal void TryCreateSceneID()
{
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 = 0;
return;
}
ulong startId = SceneId;
uint startPath = _scenePathHash;
ulong sceneId = 0;
uint scenePathHash = 0;
//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))
{
//These are all failing conditions, don't do additional checks.
}
else
{
System.Random rnd = new System.Random();
scenePathHash = gameObject.scene.path.ToLower().GetStableHash32();
sceneId = SceneId;
//Not a valid sceneId or is a duplicate.
if (scenePathHash != _scenePathHash || SceneId == 0 || IsDuplicateSceneId(SceneId))
{
/* If a scene has not been opened since an id has been
* generated then it will not be serialized in editor. The id
* would be correct in build but not if running in editor.
* Should conditions be true where scene is building without
* being opened then cancel build and request user to open and save
* scene. */
if (BuildPipeline.isBuildingPlayer)
throw new InvalidOperationException($"Networked GameObject {gameObject.name} in scene {gameObject.scene.path} is missing a SceneId. Open the scene, select the Fish-Networking menu, and choose Rebuild SceneIds. 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.");
ulong shiftedHash = (ulong)scenePathHash << 32;
ulong randomId = 0;
while (randomId == 0 || IsDuplicateSceneId(randomId))
{
uint next = (uint)(rnd.Next(int.MinValue, int.MaxValue) + int.MaxValue);
/* Since the collection is lost when a scene loads the it's possible to
* have a sceneid from another scene. Because of this the scene path is
* inserted into the sceneid. */
randomId = (next & 0xFFFFFFFF) | shiftedHash;
}
sceneId = randomId;
}
}
bool idChanged = (sceneId != startId);
bool pathChanged = (startPath != scenePathHash);
//If either changed then dirty and set.
if (idChanged || pathChanged)
{
//Set dirty so changes will be saved.
EditorUtility.SetDirty(this);
/* Add to sceneIds collection. This must be done
* even if a new sceneId was not generated because
* the collection information is lost when the
* scene is existed. Essentially, it gets repopulated
* when the scene is re-opened. */
SceneId = sceneId;
_scenePathHash = scenePathHash;
}
}
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;
}
/// <summary>
/// Returns if the Id used is a sceneId already belonging to another object.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
private bool IsDuplicateSceneId(ulong id)
{
//Find all nobs in scene.
_sceneNetworkObjects = GameObject.FindObjectsOfType<NetworkObject>().ToList();
foreach (NetworkObject nob in _sceneNetworkObjects)
{
if (nob != null && nob != this && nob.SceneId == id)
return true;
}
//If here all checks pass.
return false;
}
private void ReferenceIds_OnValidate()
{
TryCreateSceneID();
}
private void ReferenceIds_Reset()
{
TryCreateSceneID();
}
#endif
}
}