Seven is the number.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
12 KiB

3 years ago
  1. // ----------------------------------------------------------------------------
  2. // <copyright file="PhotonEditorUtils.cs" company="Exit Games GmbH">
  3. // PhotonNetwork Framework for Unity - Copyright (C) 2018 Exit Games GmbH
  4. // </copyright>
  5. // <summary>
  6. // Unity Editor Utils
  7. // </summary>
  8. // <author>developer@exitgames.com</author>
  9. // ----------------------------------------------------------------------------
  10. #pragma warning disable 618 // Deprecation warnings
  11. #if UNITY_2017_4_OR_NEWER
  12. #define SUPPORTED_UNITY
  13. #endif
  14. #if UNITY_EDITOR
  15. namespace Photon.Realtime
  16. {
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using UnityEditor;
  21. using UnityEngine;
  22. using System.IO;
  23. using System.Text;
  24. using UnityEngine.Networking;
  25. [InitializeOnLoad]
  26. public static class PhotonEditorUtils
  27. {
  28. /// <summary>True if the ChatClient of the Photon Chat API is available. If so, the editor may (e.g.) show additional options in settings.</summary>
  29. public static bool HasChat;
  30. /// <summary>True if the VoiceClient of the Photon Voice API is available. If so, the editor may (e.g.) show additional options in settings.</summary>
  31. public static bool HasVoice;
  32. public static bool HasPun;
  33. /// <summary>True if the PhotonEditorUtils checked the available products / APIs. If so, the editor may (e.g.) show additional options in settings.</summary>
  34. public static bool HasCheckedProducts;
  35. static PhotonEditorUtils()
  36. {
  37. HasVoice = Type.GetType("Photon.Voice.VoiceClient, Assembly-CSharp") != null || Type.GetType("Photon.Voice.VoiceClient, Assembly-CSharp-firstpass") != null || Type.GetType("Photon.Voice.VoiceClient, PhotonVoice.API") != null;
  38. HasChat = Type.GetType("Photon.Chat.ChatClient, Assembly-CSharp") != null || Type.GetType("Photon.Chat.ChatClient, Assembly-CSharp-firstpass") != null || Type.GetType("Photon.Chat.ChatClient, PhotonChat") != null;
  39. HasPun = Type.GetType("Photon.Pun.PhotonNetwork, Assembly-CSharp") != null || Type.GetType("Photon.Pun.PhotonNetwork, Assembly-CSharp-firstpass") != null || Type.GetType("Photon.Pun.PhotonNetwork, PhotonUnityNetworking") != null;
  40. PhotonEditorUtils.HasCheckedProducts = true;
  41. if (EditorPrefs.HasKey("DisablePun") && EditorPrefs.GetBool("DisablePun"))
  42. {
  43. HasPun = false;
  44. }
  45. if (HasPun)
  46. {
  47. // MOUNTING SYMBOLS
  48. #if !PHOTON_UNITY_NETWORKING
  49. AddScriptingDefineSymbolToAllBuildTargetGroups("PHOTON_UNITY_NETWORKING");
  50. #endif
  51. #if !PUN_2_0_OR_NEWER
  52. AddScriptingDefineSymbolToAllBuildTargetGroups("PUN_2_0_OR_NEWER");
  53. #endif
  54. #if !PUN_2_OR_NEWER
  55. AddScriptingDefineSymbolToAllBuildTargetGroups("PUN_2_OR_NEWER");
  56. #endif
  57. #if !PUN_2_19_OR_NEWER
  58. AddScriptingDefineSymbolToAllBuildTargetGroups("PUN_2_19_OR_NEWER");
  59. #endif
  60. }
  61. }
  62. /// <summary>
  63. /// Adds a given scripting define symbol to all build target groups
  64. /// You can see all scripting define symbols ( not the internal ones, only the one for this project), in the PlayerSettings inspector
  65. /// </summary>
  66. /// <param name="defineSymbol">Define symbol.</param>
  67. public static void AddScriptingDefineSymbolToAllBuildTargetGroups(string defineSymbol)
  68. {
  69. foreach (BuildTarget target in Enum.GetValues(typeof(BuildTarget)))
  70. {
  71. BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target);
  72. if (group == BuildTargetGroup.Unknown)
  73. {
  74. continue;
  75. }
  76. var defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(group).Split(';').Select(d => d.Trim()).ToList();
  77. if (!defineSymbols.Contains(defineSymbol))
  78. {
  79. defineSymbols.Add(defineSymbol);
  80. try
  81. {
  82. PlayerSettings.SetScriptingDefineSymbolsForGroup(group, string.Join(";", defineSymbols.ToArray()));
  83. }
  84. catch (Exception e)
  85. {
  86. Debug.Log("Could not set Photon " + defineSymbol + " defines for build target: " + target + " group: " + group + " " + e);
  87. }
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// Removes PUN2's Script Define Symbols from project
  93. /// </summary>
  94. public static void CleanUpPunDefineSymbols()
  95. {
  96. foreach (BuildTarget target in Enum.GetValues(typeof(BuildTarget)))
  97. {
  98. BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target);
  99. if (group == BuildTargetGroup.Unknown)
  100. {
  101. continue;
  102. }
  103. var defineSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(group)
  104. .Split(';')
  105. .Select(d => d.Trim())
  106. .ToList();
  107. List<string> newDefineSymbols = new List<string>();
  108. foreach (var symbol in defineSymbols)
  109. {
  110. if ("PHOTON_UNITY_NETWORKING".Equals(symbol) || symbol.StartsWith("PUN_2_"))
  111. {
  112. continue;
  113. }
  114. newDefineSymbols.Add(symbol);
  115. }
  116. try
  117. {
  118. PlayerSettings.SetScriptingDefineSymbolsForGroup(group, string.Join(";", newDefineSymbols.ToArray()));
  119. }
  120. catch (Exception e)
  121. {
  122. Debug.LogErrorFormat("Could not set clean up PUN2's define symbols for build target: {0} group: {1}, {2}", target, group, e);
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Gets the parent directory of a path. Recursive Function, will return null if parentName not found
  128. /// </summary>
  129. /// <returns>The parent directory</returns>
  130. /// <param name="path">Path.</param>
  131. /// <param name="parentName">Parent name.</param>
  132. public static string GetParent(string path, string parentName)
  133. {
  134. var dir = new DirectoryInfo(path);
  135. if (dir.Parent == null)
  136. {
  137. return null;
  138. }
  139. if (string.IsNullOrEmpty(parentName))
  140. {
  141. return dir.Parent.FullName;
  142. }
  143. if (dir.Parent.Name == parentName)
  144. {
  145. return dir.Parent.FullName;
  146. }
  147. return GetParent(dir.Parent.FullName, parentName);
  148. }
  149. /// <summary>
  150. /// Check if a GameObject is a prefab asset or part of a prefab asset, as opposed to an instance in the scene hierarchy
  151. /// </summary>
  152. /// <returns><c>true</c>, if a prefab asset or part of it, <c>false</c> otherwise.</returns>
  153. /// <param name="go">The GameObject to check</param>
  154. public static bool IsPrefab(GameObject go)
  155. {
  156. #if UNITY_2018_3_OR_NEWER
  157. return UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage(go) != null || EditorUtility.IsPersistent(go);
  158. #else
  159. return EditorUtility.IsPersistent(go);
  160. #endif
  161. }
  162. //https://forum.unity.com/threads/using-unitywebrequest-in-editor-tools.397466/#post-4485181
  163. public static void StartCoroutine(System.Collections.IEnumerator update)
  164. {
  165. EditorApplication.CallbackFunction closureCallback = null;
  166. closureCallback = () =>
  167. {
  168. try
  169. {
  170. if (update.MoveNext() == false)
  171. {
  172. EditorApplication.update -= closureCallback;
  173. }
  174. }
  175. catch (Exception ex)
  176. {
  177. Debug.LogException(ex);
  178. EditorApplication.update -= closureCallback;
  179. }
  180. };
  181. EditorApplication.update += closureCallback;
  182. }
  183. public static System.Collections.IEnumerator HttpPost(string url, Dictionary<string, string> headers, byte[] payload, Action<string> successCallback, Action<string> errorCallback)
  184. {
  185. using (UnityWebRequest w = new UnityWebRequest(url, "POST"))
  186. {
  187. if (payload != null)
  188. {
  189. w.uploadHandler = new UploadHandlerRaw(payload);
  190. }
  191. w.downloadHandler = new DownloadHandlerBuffer();
  192. if (headers != null)
  193. {
  194. foreach (var header in headers)
  195. {
  196. w.SetRequestHeader(header.Key, header.Value);
  197. }
  198. }
  199. #if UNITY_2017_2_OR_NEWER
  200. yield return w.SendWebRequest();
  201. #else
  202. yield return w.Send();
  203. #endif
  204. while (w.isDone == false)
  205. yield return null;
  206. #if UNITY_2020_2_OR_NEWER
  207. if (w.result == UnityWebRequest.Result.ProtocolError || w.result == UnityWebRequest.Result.ConnectionError || w.result == UnityWebRequest.Result.DataProcessingError)
  208. #elif UNITY_2017_1_OR_NEWER
  209. if (w.isNetworkError || w.isHttpError)
  210. #endif
  211. {
  212. if (errorCallback != null)
  213. {
  214. errorCallback(w.error);
  215. }
  216. }
  217. else
  218. {
  219. if (successCallback != null)
  220. {
  221. successCallback(w.downloadHandler.text);
  222. }
  223. }
  224. }
  225. }
  226. /// <summary>
  227. /// Creates a Foldout using a toggle with (GUIStyle)"Foldout") and a separate label. This is a workaround for 2019.3 foldout arrows not working.
  228. /// </summary>
  229. /// <param name="isExpanded"></param>
  230. /// <param name="label"></param>
  231. /// <returns>Returns the new isExpanded value.</returns>
  232. public static bool Foldout(this SerializedProperty isExpanded, GUIContent label)
  233. {
  234. var rect = EditorGUILayout.GetControlRect();
  235. bool newvalue = EditorGUI.Toggle(new Rect(rect) { xMin = rect.xMin + 2 }, GUIContent.none, isExpanded.boolValue, (GUIStyle)"Foldout");
  236. EditorGUI.LabelField(new Rect(rect) { xMin = rect.xMin + 15 }, label);
  237. if (newvalue != isExpanded.boolValue)
  238. {
  239. isExpanded.boolValue = newvalue;
  240. isExpanded.serializedObject.ApplyModifiedProperties();
  241. }
  242. return newvalue;
  243. }
  244. /// <summary>
  245. /// Creates a Foldout using a toggle with (GUIStyle)"Foldout") and a separate label. This is a workaround for 2019.3 foldout arrows not working.
  246. /// </summary>
  247. /// <param name="isExpanded"></param>
  248. /// <param name="label"></param>
  249. /// <returns>Returns the new isExpanded value.</returns>
  250. public static bool Foldout(this bool isExpanded, GUIContent label)
  251. {
  252. var rect = EditorGUILayout.GetControlRect();
  253. bool newvalue = EditorGUI.Toggle(new Rect(rect) { xMin = rect.xMin + 2 }, GUIContent.none, isExpanded, (GUIStyle)"Foldout");
  254. EditorGUI.LabelField(new Rect(rect) { xMin = rect.xMin + 15 }, label);
  255. return newvalue;
  256. }
  257. }
  258. public class CleanUpDefinesOnPunDelete : UnityEditor.AssetModificationProcessor
  259. {
  260. public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions rao)
  261. {
  262. if ("Assets/Photon/PhotonUnityNetworking".Equals(assetPath))
  263. {
  264. PhotonEditorUtils.CleanUpPunDefineSymbols();
  265. }
  266. return AssetDeleteResult.DidNotDelete;
  267. }
  268. }
  269. }
  270. #endif