NetworkScenePostProcess.cs 5.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEditor;
  4. using UnityEditor.Callbacks;
  5. using UnityEngine;
  6. namespace Mirror
  7. {
  8. public class NetworkScenePostProcess : MonoBehaviour
  9. {
  10. [PostProcessScene]
  11. public static void OnPostProcessScene()
  12. {
  13. // find all NetworkIdentities in all scenes
  14. // => can't limit it to GetActiveScene() because that wouldn't work
  15. // for additive scene loads (the additively loaded scene is never
  16. // the active scene)
  17. // => ignore DontDestroyOnLoad scene! this avoids weird situations
  18. // like in NetworkZones when we destroy the local player and
  19. // load another scene afterwards, yet the local player is still
  20. // in the FindObjectsOfType result with scene=DontDestroyOnLoad
  21. // for some reason
  22. // => OfTypeAll so disabled objects are included too
  23. // => Unity 2019 returns prefabs here too, so filter them out.
  24. IEnumerable<NetworkIdentity> identities = Resources.FindObjectsOfTypeAll<NetworkIdentity>()
  25. .Where(identity => identity.gameObject.hideFlags != HideFlags.NotEditable &&
  26. identity.gameObject.hideFlags != HideFlags.HideAndDontSave &&
  27. identity.gameObject.scene.name != "DontDestroyOnLoad" &&
  28. !Utils.IsPrefab(identity.gameObject));
  29. foreach (NetworkIdentity identity in identities)
  30. {
  31. // if we had a [ConflictComponent] attribute that would be better than this check.
  32. // also there is no context about which scene this is in.
  33. if (identity.GetComponent<NetworkManager>() != null)
  34. Debug.LogError("NetworkManager has a NetworkIdentity component. This will cause the NetworkManager object to be disabled, so it is not recommended.");
  35. // not spawned before?
  36. // OnPostProcessScene is called after additive scene loads too,
  37. // and we don't want to set main scene's objects inactive again
  38. if (!identity.isClient && !identity.isServer)
  39. {
  40. // valid scene object?
  41. // otherwise it might be an unopened scene that still has null
  42. // sceneIds. builds are interrupted if they contain 0 sceneIds,
  43. // but it's still possible that we call LoadScene in Editor
  44. // for a previously unopened scene.
  45. // (and only do SetActive if this was actually a scene object)
  46. if (identity.sceneId != 0)
  47. {
  48. PrepareSceneObject(identity);
  49. }
  50. // throwing an exception would only show it for one object
  51. // because this function would return afterwards.
  52. else
  53. {
  54. // there are two cases where sceneId == 0:
  55. // * if we have a prefab open in the prefab scene
  56. // * if an unopened scene needs resaving
  57. // show a proper error message in both cases so the user
  58. // knows what to do.
  59. string path = identity.gameObject.scene.path;
  60. if (string.IsNullOrWhiteSpace(path))
  61. Debug.LogError($"{identity.name} is currently open in Prefab Edit Mode. Please open the actual scene before launching Mirror.");
  62. else
  63. Debug.LogError($"Scene {path} needs to be opened and resaved, because the scene object {identity.name} has no valid sceneId yet.");
  64. // either way we shouldn't continue. nothing good will
  65. // happen when trying to launch with invalid sceneIds.
  66. EditorApplication.isPlaying = false;
  67. }
  68. }
  69. }
  70. }
  71. static void PrepareSceneObject(NetworkIdentity identity)
  72. {
  73. // set scene hash
  74. identity.SetSceneIdSceneHashPartInternal();
  75. // spawnable scene objects are force disabled on scene load to
  76. // ensure Start/Update/etc. aren't called until actually spawned.
  77. //
  78. // note: NetworkIdentity.OnDisable adds itself to the
  79. // spawnableObjects dictionary (only if sceneId != 0)
  80. identity.gameObject.SetActive(false);
  81. // safety check for prefabs with more than one NetworkIdentity
  82. GameObject prefabGO = PrefabUtility.GetCorrespondingObjectFromSource(identity.gameObject);
  83. if (prefabGO)
  84. {
  85. GameObject prefabRootGO = prefabGO.transform.root.gameObject;
  86. if (prefabRootGO != null && prefabRootGO.GetComponentsInChildren<NetworkIdentity>().Length > 1)
  87. Debug.LogWarning($"Prefab {prefabRootGO.name} has several NetworkIdentity components attached to itself or its children, this is not supported.");
  88. }
  89. }
  90. }
  91. }