Utils.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Security.Cryptography;
  4. using UnityEngine;
  5. using UnityEngine.Rendering;
  6. using UnityEngine.SceneManagement;
  7. namespace Mirror
  8. {
  9. // Handles network messages on client and server
  10. public delegate void NetworkMessageDelegate(NetworkConnection conn, NetworkReader reader, int channelId);
  11. // Handles requests to spawn objects on the client
  12. public delegate GameObject SpawnDelegate(Vector3 position, uint assetId);
  13. public delegate GameObject SpawnHandlerDelegate(SpawnMessage msg);
  14. // Handles requests to unspawn objects on the client
  15. public delegate void UnSpawnDelegate(GameObject spawned);
  16. // channels are const ints instead of an enum so people can add their own
  17. // channels (can't extend an enum otherwise).
  18. //
  19. // note that Mirror is slowly moving towards quake style networking which
  20. // will only require reliable for handshake, and unreliable for the rest.
  21. // so eventually we can change this to an Enum and transports shouldn't
  22. // add custom channels anymore.
  23. public static class Channels
  24. {
  25. public const int Reliable = 0; // ordered
  26. public const int Unreliable = 1; // unordered
  27. }
  28. public static class Utils
  29. {
  30. // detect headless / dedicated server mode
  31. // SystemInfo.graphicsDeviceType is never null in the editor.
  32. // UNITY_SERVER works in builds for all Unity versions 2019 LTS and later.
  33. // For Unity 2019 / 2020, there is no way to detect Server Build checkbox
  34. // state in Build Settings, so they never auto-start headless server / client.
  35. // UNITY_SERVER works in the editor in Unity 2021 LTS and later
  36. // because that's when Dedicated Server platform was added.
  37. // It is intentional for editor play mode to auto-start headless server / client
  38. // when Dedicated Server platform is selected in the editor so that editor
  39. // acts like a headless build to every extent possible for testing / debugging.
  40. public static bool IsHeadless() =>
  41. #if UNITY_SERVER
  42. true;
  43. #else
  44. SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null;
  45. #endif
  46. // detect WebGL mode
  47. public const bool IsWebGL =
  48. #if UNITY_WEBGL
  49. true;
  50. #else
  51. false;
  52. #endif
  53. // detect Debug mode
  54. public const bool IsDebug =
  55. #if DEBUG
  56. true;
  57. #else
  58. false;
  59. #endif
  60. public static uint GetTrueRandomUInt()
  61. {
  62. // use Crypto RNG to avoid having time based duplicates
  63. using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
  64. {
  65. byte[] bytes = new byte[4];
  66. rng.GetBytes(bytes);
  67. return BitConverter.ToUInt32(bytes, 0);
  68. }
  69. }
  70. public static bool IsPrefab(GameObject obj)
  71. {
  72. #if UNITY_EDITOR
  73. return UnityEditor.PrefabUtility.IsPartOfPrefabAsset(obj);
  74. #else
  75. return false;
  76. #endif
  77. }
  78. // simplified IsSceneObject check from Mirror II
  79. public static bool IsSceneObject(NetworkIdentity identity)
  80. {
  81. // original UNET / Mirror still had the IsPersistent check.
  82. // it never fires though. even for Prefabs dragged to the Scene.
  83. // (see Scene Objects example scene.)
  84. // #if UNITY_EDITOR
  85. // if (UnityEditor.EditorUtility.IsPersistent(identity.gameObject))
  86. // return false;
  87. // #endif
  88. return identity.gameObject.hideFlags != HideFlags.NotEditable &&
  89. identity.gameObject.hideFlags != HideFlags.HideAndDontSave &&
  90. identity.sceneId != 0;
  91. }
  92. public static bool IsSceneObjectWithPrefabParent(GameObject gameObject, out GameObject prefab)
  93. {
  94. prefab = null;
  95. #if UNITY_EDITOR
  96. if (!UnityEditor.PrefabUtility.IsPartOfPrefabInstance(gameObject))
  97. {
  98. return false;
  99. }
  100. prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
  101. #endif
  102. if (prefab == null)
  103. {
  104. Debug.LogError($"Failed to find prefab parent for scene object [name:{gameObject.name}]");
  105. return false;
  106. }
  107. return true;
  108. }
  109. // is a 2D point in screen? (from ummorpg)
  110. // (if width = 1024, then indices from 0..1023 are valid (=1024 indices)
  111. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  112. public static bool IsPointInScreen(Vector2 point) =>
  113. 0 <= point.x && point.x < Screen.width &&
  114. 0 <= point.y && point.y < Screen.height;
  115. // pretty print bytes as KB/MB/GB/etc. from DOTSNET
  116. // long to support > 2GB
  117. // divides by floats to return "2.5MB" etc.
  118. public static string PrettyBytes(long bytes)
  119. {
  120. // bytes
  121. if (bytes < 1024)
  122. return $"{bytes} B";
  123. // kilobytes
  124. else if (bytes < 1024L * 1024L)
  125. return $"{(bytes / 1024f):F2} KB";
  126. // megabytes
  127. else if (bytes < 1024 * 1024L * 1024L)
  128. return $"{(bytes / (1024f * 1024f)):F2} MB";
  129. // gigabytes
  130. return $"{(bytes / (1024f * 1024f * 1024f)):F2} GB";
  131. }
  132. // pretty print seconds as hours:minutes:seconds(.milliseconds/100)s.
  133. // double for long running servers.
  134. public static string PrettySeconds(double seconds)
  135. {
  136. TimeSpan t = TimeSpan.FromSeconds(seconds);
  137. string res = "";
  138. if (t.Days > 0) res += $"{t.Days}d";
  139. if (t.Hours > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Hours}h";
  140. if (t.Minutes > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Minutes}m";
  141. // 0.5s, 1.5s etc. if any milliseconds. 1s, 2s etc. if any seconds
  142. if (t.Milliseconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}.{(t.Milliseconds / 100)}s";
  143. else if (t.Seconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}s";
  144. // if the string is still empty because the value was '0', then at least
  145. // return the seconds instead of returning an empty string
  146. return res != "" ? res : "0s";
  147. }
  148. // universal .spawned function
  149. public static NetworkIdentity GetSpawnedInServerOrClient(uint netId)
  150. {
  151. // server / host mode: use the one from server.
  152. // host mode has access to all spawned.
  153. if (NetworkServer.active)
  154. {
  155. NetworkServer.spawned.TryGetValue(netId, out NetworkIdentity entry);
  156. return entry;
  157. }
  158. // client
  159. if (NetworkClient.active)
  160. {
  161. NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity entry);
  162. return entry;
  163. }
  164. return null;
  165. }
  166. // keep a GUI window in screen.
  167. // for example. if it's at x=1000 and screen is resized to w=500,
  168. // it won't get lost in the invisible area etc.
  169. public static Rect KeepInScreen(Rect rect)
  170. {
  171. // ensure min
  172. rect.x = Math.Max(rect.x, 0);
  173. rect.y = Math.Max(rect.y, 0);
  174. // ensure max
  175. rect.x = Math.Min(rect.x, Screen.width - rect.width);
  176. rect.y = Math.Min(rect.y, Screen.width - rect.height);
  177. return rect;
  178. }
  179. // create local connections pair and connect them
  180. public static void CreateLocalConnections(
  181. out LocalConnectionToClient connectionToClient,
  182. out LocalConnectionToServer connectionToServer)
  183. {
  184. connectionToServer = new LocalConnectionToServer();
  185. connectionToClient = new LocalConnectionToClient();
  186. connectionToServer.connectionToClient = connectionToClient;
  187. connectionToClient.connectionToServer = connectionToServer;
  188. }
  189. public static bool IsSceneActive(string scene)
  190. {
  191. Scene activeScene = SceneManager.GetActiveScene();
  192. return activeScene.path == scene ||
  193. activeScene.name == scene;
  194. }
  195. }
  196. }