PhotonEditor.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. // ----------------------------------------------------------------------------
  2. // <copyright file="PhotonEditor.cs" company="Exit Games GmbH">
  3. // PhotonNetwork Framework for Unity - Copyright (C) 2018 Exit Games GmbH
  4. // </copyright>
  5. // <summary>
  6. // MenuItems and in-Editor scripts for PhotonNetwork.
  7. // </summary>
  8. // <author>developer@exitgames.com</author>
  9. // ----------------------------------------------------------------------------
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Reflection;
  14. using UnityEditor;
  15. using UnityEditor.Callbacks;
  16. using UnityEditor.Compilation;
  17. using UnityEngine;
  18. namespace Photon.Pun
  19. {
  20. using Realtime;
  21. public class PunWizardText
  22. {
  23. public string WindowTitle = "PUN Wizard";
  24. public string SetupWizardWarningTitle = "Warning";
  25. public string SetupWizardWarningMessage = "You have not yet run the Photon setup wizard! Your game won't be able to connect. See Windows -> Photon Unity Networking.";
  26. public string MainMenuButton = "Main Menu";
  27. public string SetupWizardTitle = "PUN Setup";
  28. public string SetupWizardInfo = "Thanks for importing Photon Unity Networking.\nThis window should set you up.\n\n<b>-</b> To use an existing Photon Cloud App, enter your AppId.\n<b>-</b> To register an account or access an existing one, enter the account's mail address.\n<b>-</b> To use Photon OnPremise, skip this step.";
  29. public string EmailOrAppIdLabel = "AppId or Email";
  30. public string AlreadyRegisteredInfo = "The email is registered so we can't fetch your AppId (without password).\n\nPlease login online to get your AppId and paste it above.";
  31. public string SkipRegistrationInfo = "Skipping? No problem:\nEdit your server settings in the PhotonServerSettings file.";
  32. public string RegisteredNewAccountInfo = "We created a (free) account and fetched you an AppId.\nWelcome. Your PUN project is setup.";
  33. public string AppliedToSettingsInfo = "Your AppId is now applied to this project.";
  34. public string SetupCompleteInfo = "<b>Done!</b>\nAll connection settings can be edited in the <b>PhotonServerSettings</b> now.\nHave a look.";
  35. public string CloseWindowButton = "Close";
  36. public string SkipButton = "Skip";
  37. public string SetupButton = "Setup Project";
  38. public string CancelButton = "Cancel";
  39. public string PUNWizardLabel = "PUN Wizard";
  40. public string SettingsButton = "Settings:";
  41. public string SetupServerCloudLabel = "Setup wizard for setting up your own server or the cloud.";
  42. public string WarningPhotonDisconnect = "Disconnecting PUN due to recompile. Exit PlayMode.";
  43. public string StartButton = "Start";
  44. public string LocateSettingsButton = "Locate PhotonServerSettings";
  45. public string SettingsHighlightLabel = "Highlights the used photon settings file in the project.";
  46. public string DocumentationLabel = "Documentation:";
  47. public string OpenPDFText = "Reference PDF";
  48. public string OpenPDFTooltip = "Opens the local documentation pdf.";
  49. public string OpenDevNetText = "Doc Pages / Manual";
  50. public string OpenDevNetTooltip = "Online documentation for Photon.";
  51. public string OpenCloudDashboardText = "Cloud Dashboard Login";
  52. public string OpenCloudDashboardTooltip = "Review Cloud App information and statistics.";
  53. public string OpenForumText = "Open Forum";
  54. public string OpenForumTooltip = "Online support for Photon.";
  55. public string OkButton = "Ok";
  56. public string OwnHostCloudCompareLabel = "How 'my own host' compares to 'cloud'.";
  57. public string ComparisonPageButton = "Cloud versus OnPremise";
  58. public string ConnectionTitle = "Connecting";
  59. public string ConnectionInfo = "Connecting to the account service...";
  60. public string ErrorTextTitle = "Error";
  61. public string IncorrectRPCListTitle = "Warning: RPC-list becoming incompatible!";
  62. public string IncorrectRPCListLabel = "Your project's RPC-list is full, so we can't add some RPCs just compiled.\n\nBy removing outdated RPCs, the list will be long enough but incompatible with older client builds!\n\nMake sure you change the game version where you use PhotonNetwork.ConnectUsingSettings().";
  63. public string RemoveOutdatedRPCsLabel = "Remove outdated RPCs";
  64. public string FullRPCListTitle = "Warning: RPC-list is full!";
  65. public string FullRPCListLabel = "Your project's RPC-list is too long for PUN.\n\nYou can change PUN's source to use short-typed RPC index. Look for comments 'LIMITS RPC COUNT'\n\nAlternatively, remove some RPC methods (use more parameters per RPC maybe).\n\nAfter a RPC-list refresh, make sure you change the game version where you use PhotonNetwork.ConnectUsingSettings().";
  66. public string SkipRPCListUpdateLabel = "Skip RPC-list update";
  67. public string PUNNameReplaceTitle = "Warning: RPC-list Compatibility";
  68. public string PUNNameReplaceLabel = "PUN replaces RPC names with numbers by using the RPC-list. All clients must use the same list for that.\n\nClearing it most likely makes your client incompatible with previous versions! Change your game version or make sure the RPC-list matches other clients.";
  69. public string RPCListCleared = "Clear RPC-list";
  70. public string ServerSettingsCleanedWarning = "Cleared the PhotonServerSettings.RpcList, which breaks compatibility with older builds. You should update the \"App Version\" in the PhotonServerSettings to avoid issues.";
  71. public string WizardMainWindowInfo = "This window should help you find important settings for PUN, as well as documentation.";
  72. }
  73. public class PhotonEditor : EditorWindow
  74. {
  75. protected static Type WindowType = typeof(PhotonEditor);
  76. protected Vector2 scrollPos = Vector2.zero;
  77. private readonly Vector2 preferredSize = new Vector2(350, 400);
  78. private static Texture2D BackgroundImage;
  79. public static PunWizardText CurrentLang = new PunWizardText();
  80. /// <summary>
  81. /// third parties custom token
  82. /// </summary>
  83. public static string CustomToken = null;
  84. /// <summary>
  85. /// third parties custom context
  86. /// </summary>
  87. public static string CustomContext = null;
  88. protected static string DocumentationLocation = "Assets/Photon/PhotonNetworking-Documentation.pdf";
  89. protected static string UrlFreeLicense = "https://dashboard.photonengine.com/en-US/SelfHosted";
  90. public const string UrlDevNet = "https://doc.photonengine.com/en-us/pun/v2";
  91. protected static string UrlForum = "https://forum.photonengine.com";
  92. protected static string UrlCompare = "https://doc.photonengine.com/en-us/realtime/current/getting-started/onpremise-or-saas";
  93. protected static string UrlHowToSetup = "https://doc.photonengine.com/en-us/onpremise/current/getting-started/photon-server-in-5min";
  94. protected static string UrlAppIDExplained = "https://doc.photonengine.com/en-us/realtime/current/getting-started/obtain-your-app-id";
  95. public const string UrlCloudDashboard = "https://dashboard.photonengine.com/en-US/account/signin?email=";
  96. public const string UrlPunSettings = "https://doc.photonengine.com/en-us/pun/v2/getting-started/initial-setup"; // the SeverSettings class has this url directly in it's HelpURL attribute.
  97. private enum PhotonSetupStates
  98. {
  99. MainUi,
  100. RegisterForPhotonCloud,
  101. EmailAlreadyRegistered,
  102. GoEditPhotonServerSettings,
  103. EmailRegistrationPending
  104. }
  105. private bool isSetupWizard = false;
  106. private PhotonSetupStates photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
  107. private bool minimumInput = false;
  108. private bool useMail = false;
  109. private bool useAppId = false;
  110. private bool useSkip = false;
  111. private bool highlightedSettings = false;
  112. private bool close = false;
  113. private string mailOrAppId = string.Empty;
  114. private static double lastWarning = 0;
  115. private static bool postInspectorUpdate;
  116. [MenuItem("Window/Photon Unity Networking/PUN Wizard &p", false, 0)]
  117. protected static void MenuItemOpenWizard()
  118. {
  119. PhotonEditor win = GetWindow<PhotonEditor>(false, CurrentLang.WindowTitle, true);
  120. if (win == null)
  121. {
  122. return;
  123. }
  124. win.photonSetupState = PhotonSetupStates.MainUi;
  125. win.isSetupWizard = false;
  126. }
  127. [MenuItem("Window/Photon Unity Networking/Highlight Server Settings %#&p", false, 1)]
  128. protected static void MenuItemHighlightSettings()
  129. {
  130. HighlightSettings();
  131. }
  132. [UnityEditor.InitializeOnLoadMethod]
  133. public static void InitializeOnLoadMethod()
  134. {
  135. //Debug.Log("InitializeOnLoadMethod()");
  136. EditorApplication.delayCall += OnDelayCall;
  137. }
  138. // used to register for various events (post-load)
  139. private static void OnDelayCall()
  140. {
  141. //Debug.Log("OnDelayCall()");
  142. postInspectorUpdate = true;
  143. EditorApplication.playModeStateChanged -= PlayModeStateChanged;
  144. EditorApplication.playModeStateChanged += PlayModeStateChanged;
  145. #if UNITY_2021_1_OR_NEWER
  146. CompilationPipeline.compilationStarted -= OnCompileStarted21;
  147. CompilationPipeline.compilationStarted += OnCompileStarted21;
  148. #else
  149. CompilationPipeline.assemblyCompilationStarted -= OnCompileStarted;
  150. CompilationPipeline.assemblyCompilationStarted += OnCompileStarted;
  151. #endif
  152. #if (UNITY_2018 || UNITY_2018_1_OR_NEWER)
  153. EditorApplication.projectChanged -= OnProjectChanged;
  154. EditorApplication.projectChanged += OnProjectChanged;
  155. #else
  156. EditorApplication.projectWindowChanged -= OnProjectChanged;
  157. EditorApplication.projectWindowChanged += OnProjectChanged;
  158. #endif
  159. if (!EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode)
  160. {
  161. OnProjectChanged(); // call this initially from here, as the project change events happened earlier (on start of the Editor)
  162. PhotonEditor.UpdateRpcList();
  163. }
  164. }
  165. // called in editor, opens wizard for initial setup, keeps scene PhotonViews up to date and closes connections when compiling (to avoid issues)
  166. private static void OnProjectChanged()
  167. {
  168. PhotonEditorUtils.ProjectChangedWasCalled = true;
  169. // Prevent issues with Unity Cloud Builds where ServerSettings are not found.
  170. // Also, within the context of a Unity Cloud Build, ServerSettings is already present anyway.
  171. #if UNITY_CLOUD_BUILD
  172. return;
  173. #endif
  174. if (PhotonNetwork.PhotonServerSettings == null || PhotonNetwork.PhotonServerSettings.AppSettings == null || string.IsNullOrEmpty(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdRealtime))
  175. {
  176. PhotonNetwork.LoadOrCreateSettings(true);
  177. }
  178. if (PhotonNetwork.PhotonServerSettings == null)
  179. {
  180. // the PhotonServerSettings are loaded or created. If both fails, the Editor should probably not run (anymore).
  181. return;
  182. }
  183. PunSceneSettings.SanitizeSceneSettings();
  184. // serverSetting is null when the file gets deleted. otherwise, the wizard should only run once and only if hosting option is not (yet) set
  185. if (!PhotonNetwork.PhotonServerSettings.DisableAutoOpenWizard)
  186. {
  187. ShowRegistrationWizard();
  188. PhotonNetwork.PhotonServerSettings.DisableAutoOpenWizard = true;
  189. PhotonEditor.SaveSettings();
  190. }
  191. }
  192. #if UNITY_2021_1_OR_NEWER
  193. private static void OnCompileStarted21(object obj)
  194. {
  195. OnCompileStarted(obj as string);
  196. }
  197. #endif
  198. private static void OnCompileStarted(string obj)
  199. {
  200. if (PhotonNetwork.IsConnected)
  201. {
  202. // log warning, unless there was one recently
  203. if (EditorApplication.timeSinceStartup - lastWarning > 3)
  204. {
  205. Debug.LogWarning(CurrentLang.WarningPhotonDisconnect);
  206. lastWarning = EditorApplication.timeSinceStartup;
  207. }
  208. PhotonNetwork.Disconnect();
  209. PhotonNetwork.NetworkingClient.LoadBalancingPeer.DispatchIncomingCommands();
  210. #if UNITY_2019_4_OR_NEWER && UNITY_EDITOR
  211. EditorApplication.ExitPlaymode();
  212. #endif
  213. }
  214. }
  215. [DidReloadScripts]
  216. private static void OnDidReloadScripts()
  217. {
  218. //Debug.Log("OnDidReloadScripts() postInspectorUpdate: "+postInspectorUpdate + " isPlayingOrWillChangePlaymode: "+EditorApplication.isPlayingOrWillChangePlaymode);
  219. if (postInspectorUpdate && !EditorApplication.isPlayingOrWillChangePlaymode)
  220. {
  221. PhotonEditor.UpdateRpcList(); // could be called when compilation finished (instead of when reload / compile starts)
  222. }
  223. }
  224. private static void PlayModeStateChanged(PlayModeStateChange state)
  225. {
  226. //Debug.Log("PlayModeStateChanged");
  227. if (EditorApplication.isPlaying || !EditorApplication.isPlayingOrWillChangePlaymode)
  228. {
  229. return;
  230. }
  231. if (string.IsNullOrEmpty(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdRealtime) && !PhotonNetwork.PhotonServerSettings.AppSettings.IsMasterServerAddress)
  232. {
  233. EditorUtility.DisplayDialog(CurrentLang.SetupWizardWarningTitle, CurrentLang.SetupWizardWarningMessage, CurrentLang.OkButton);
  234. }
  235. }
  236. #region GUI and Wizard
  237. // setup per window
  238. public PhotonEditor()
  239. {
  240. this.minSize = this.preferredSize;
  241. }
  242. protected void Awake()
  243. {
  244. // check if some appid is set. if so, we can avoid registration calls.
  245. if (PhotonNetwork.PhotonServerSettings != null && PhotonNetwork.PhotonServerSettings.AppSettings != null && !string.IsNullOrEmpty(PhotonNetwork.PhotonServerSettings.AppSettings.AppIdRealtime))
  246. {
  247. this.mailOrAppId = PhotonNetwork.PhotonServerSettings.AppSettings.AppIdRealtime;
  248. }
  249. }
  250. /// <summary>Creates an Editor window, showing the cloud-registration wizard for Photon (entry point to setup PUN).</summary>
  251. protected static void ShowRegistrationWizard()
  252. {
  253. PhotonEditor win = GetWindow(WindowType, false, CurrentLang.WindowTitle, true) as PhotonEditor;
  254. if (win == null)
  255. {
  256. return;
  257. }
  258. win.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
  259. win.isSetupWizard = true;
  260. }
  261. // Window Update() callback. On-demand, when Window is open
  262. protected void Update()
  263. {
  264. if (this.close)
  265. {
  266. this.Close();
  267. }
  268. }
  269. protected virtual void OnGUI()
  270. {
  271. if (BackgroundImage == null)
  272. {
  273. string[] paths = AssetDatabase.FindAssets("PunGradient t:Texture2D");
  274. if (paths != null && paths.Length > 0)
  275. {
  276. BackgroundImage = AssetDatabase.LoadAssetAtPath<Texture2D>(AssetDatabase.GUIDToAssetPath(paths[0]));
  277. }
  278. }
  279. PhotonSetupStates oldGuiState = this.photonSetupState; // used to fix an annoying Editor input field issue: wont refresh until focus is changed.
  280. GUI.SetNextControlName(string.Empty);
  281. this.scrollPos = GUILayout.BeginScrollView(this.scrollPos);
  282. if (this.photonSetupState == PhotonSetupStates.MainUi)
  283. {
  284. this.UiMainWizard();
  285. }
  286. else
  287. {
  288. EditorGUI.BeginDisabledGroup(this.photonSetupState == PhotonSetupStates.EmailRegistrationPending);
  289. this.UiSetupApp();
  290. EditorGUI.EndDisabledGroup();
  291. }
  292. GUILayout.EndScrollView();
  293. if (oldGuiState != this.photonSetupState)
  294. {
  295. GUI.FocusControl(string.Empty);
  296. }
  297. }
  298. private string emailSentToAccount;
  299. private bool emailSentToAccountIsRegistered;
  300. protected virtual void UiSetupApp()
  301. {
  302. GUI.skin.label.wordWrap = true;
  303. if (!this.isSetupWizard)
  304. {
  305. GUILayout.BeginHorizontal();
  306. GUILayout.FlexibleSpace();
  307. if (GUILayout.Button(CurrentLang.MainMenuButton, GUILayout.ExpandWidth(false)))
  308. {
  309. this.photonSetupState = PhotonSetupStates.MainUi;
  310. }
  311. GUILayout.EndHorizontal();
  312. }
  313. // setup header
  314. this.UiTitleBox(CurrentLang.SetupWizardTitle, BackgroundImage);
  315. // setup info text
  316. GUI.skin.label.richText = true;
  317. GUILayout.Label(CurrentLang.SetupWizardInfo);
  318. // input of appid or mail
  319. EditorGUILayout.Separator();
  320. GUILayout.Label(CurrentLang.EmailOrAppIdLabel);
  321. this.minimumInput = false;
  322. this.useMail = false;
  323. this.useAppId = false;
  324. this.mailOrAppId = EditorGUILayout.TextField(this.mailOrAppId);
  325. if (!string.IsNullOrEmpty(this.mailOrAppId))
  326. {
  327. this.mailOrAppId = this.mailOrAppId.Trim(); // note: we trim all input
  328. if (AccountService.IsValidEmail(this.mailOrAppId))
  329. {
  330. // input should be a mail address
  331. this.useMail = true;
  332. // check if the current input equals earlier input, which is known to be registered already
  333. this.minimumInput = !this.mailOrAppId.Equals(this.emailSentToAccount) || !this.emailSentToAccountIsRegistered;
  334. }
  335. else if (ServerSettings.IsAppId(this.mailOrAppId))
  336. {
  337. // this should be an appId
  338. this.minimumInput = true;
  339. this.useAppId = true;
  340. }
  341. }
  342. // button to skip setup
  343. GUILayout.BeginHorizontal();
  344. GUILayout.FlexibleSpace();
  345. if (GUILayout.Button(CurrentLang.SkipButton, GUILayout.Width(100)))
  346. {
  347. this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
  348. this.useSkip = true;
  349. this.useMail = false;
  350. this.useAppId = false;
  351. }
  352. // SETUP button
  353. EditorGUI.BeginDisabledGroup(!this.minimumInput);
  354. if (GUILayout.Button(CurrentLang.SetupButton, GUILayout.Width(100)))
  355. {
  356. this.useSkip = false;
  357. GUIUtility.keyboardControl = 0;
  358. if (this.useMail)
  359. {
  360. this.RegisterWithEmail(this.mailOrAppId); // sets state
  361. }
  362. else if (this.useAppId)
  363. {
  364. this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
  365. Undo.RecordObject(PhotonNetwork.PhotonServerSettings, "Update PhotonServerSettings for PUN");
  366. PhotonNetwork.PhotonServerSettings.UseCloud(this.mailOrAppId);
  367. PhotonEditor.SaveSettings();
  368. }
  369. }
  370. EditorGUI.EndDisabledGroup();
  371. GUILayout.FlexibleSpace();
  372. GUILayout.EndHorizontal();
  373. // existing account needs to fetch AppId online
  374. if (this.photonSetupState == PhotonSetupStates.EmailAlreadyRegistered)
  375. {
  376. // button to open dashboard and get the AppId
  377. GUILayout.Space(15);
  378. GUILayout.Label(CurrentLang.AlreadyRegisteredInfo);
  379. GUILayout.BeginHorizontal();
  380. GUILayout.FlexibleSpace();
  381. if (GUILayout.Button(new GUIContent(CurrentLang.OpenCloudDashboardText, CurrentLang.OpenCloudDashboardTooltip), GUILayout.Width(205)))
  382. {
  383. Application.OpenURL(string.Concat(UrlCloudDashboard, Uri.EscapeUriString(this.mailOrAppId)));
  384. this.mailOrAppId = string.Empty;
  385. }
  386. GUILayout.FlexibleSpace();
  387. GUILayout.EndHorizontal();
  388. }
  389. else if (this.photonSetupState == PhotonSetupStates.GoEditPhotonServerSettings)
  390. {
  391. if (!this.highlightedSettings)
  392. {
  393. this.highlightedSettings = true;
  394. HighlightSettings();
  395. }
  396. GUILayout.Space(15);
  397. if (this.useSkip)
  398. {
  399. GUILayout.Label(CurrentLang.SkipRegistrationInfo);
  400. }
  401. else if (this.useMail)
  402. {
  403. GUILayout.Label(CurrentLang.RegisteredNewAccountInfo);
  404. }
  405. else if (this.useAppId)
  406. {
  407. GUILayout.Label(CurrentLang.AppliedToSettingsInfo);
  408. }
  409. // setup-complete info
  410. GUILayout.Space(15);
  411. GUILayout.Label(CurrentLang.SetupCompleteInfo);
  412. // close window (done)
  413. GUILayout.BeginHorizontal();
  414. GUILayout.FlexibleSpace();
  415. if (GUILayout.Button(CurrentLang.CloseWindowButton, GUILayout.Width(205)))
  416. {
  417. this.close = true;
  418. }
  419. GUILayout.FlexibleSpace();
  420. GUILayout.EndHorizontal();
  421. }
  422. GUI.skin.label.richText = false;
  423. }
  424. private void UiTitleBox(string title, Texture2D bgIcon)
  425. {
  426. GUIStyle bgStyle = EditorGUIUtility.isProSkin ? new GUIStyle(GUI.skin.GetStyle("Label")) : new GUIStyle(GUI.skin.GetStyle("WhiteLabel"));
  427. bgStyle.padding = new RectOffset(10, 10, 10, 10);
  428. bgStyle.fontSize = 22;
  429. bgStyle.fontStyle = FontStyle.Bold;
  430. if (bgIcon != null)
  431. {
  432. bgStyle.normal.background = bgIcon;
  433. }
  434. EditorGUILayout.BeginHorizontal();
  435. GUILayout.FlexibleSpace();
  436. EditorGUILayout.EndHorizontal();
  437. Rect scale = GUILayoutUtility.GetLastRect();
  438. scale.height = 44;
  439. GUI.Label(scale, title, bgStyle);
  440. GUILayout.Space(scale.height + 5);
  441. }
  442. protected virtual void UiMainWizard()
  443. {
  444. GUILayout.Space(15);
  445. // title
  446. this.UiTitleBox(CurrentLang.PUNWizardLabel, BackgroundImage);
  447. EditorGUILayout.BeginVertical(new GUIStyle() { padding = new RectOffset(10, 10, 10, 10) });
  448. // wizard info text
  449. GUILayout.Label(CurrentLang.WizardMainWindowInfo, new GUIStyle("Label") { wordWrap = true });
  450. GUILayout.Space(15);
  451. // settings button
  452. GUILayout.Label(CurrentLang.SettingsButton, EditorStyles.boldLabel);
  453. if (GUILayout.Button(new GUIContent(CurrentLang.LocateSettingsButton, CurrentLang.SettingsHighlightLabel)))
  454. {
  455. HighlightSettings();
  456. }
  457. if (GUILayout.Button(new GUIContent(CurrentLang.OpenCloudDashboardText, CurrentLang.OpenCloudDashboardTooltip)))
  458. {
  459. Application.OpenURL(UrlCloudDashboard + Uri.EscapeUriString(this.mailOrAppId));
  460. }
  461. if (GUILayout.Button(new GUIContent(CurrentLang.SetupButton, CurrentLang.SetupServerCloudLabel)))
  462. {
  463. this.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
  464. }
  465. GUILayout.Space(15);
  466. // documentation
  467. GUILayout.Label(CurrentLang.DocumentationLabel, EditorStyles.boldLabel);
  468. if (GUILayout.Button(new GUIContent(CurrentLang.OpenPDFText, CurrentLang.OpenPDFTooltip)))
  469. {
  470. EditorUtility.OpenWithDefaultApp(DocumentationLocation);
  471. }
  472. if (GUILayout.Button(new GUIContent(CurrentLang.OpenDevNetText, CurrentLang.OpenDevNetTooltip)))
  473. {
  474. Application.OpenURL(UrlDevNet);
  475. }
  476. GUI.skin.label.wordWrap = true;
  477. GUILayout.Label(CurrentLang.OwnHostCloudCompareLabel);
  478. if (GUILayout.Button(CurrentLang.ComparisonPageButton))
  479. {
  480. Application.OpenURL(UrlCompare);
  481. }
  482. if (GUILayout.Button(new GUIContent(CurrentLang.OpenForumText, CurrentLang.OpenForumTooltip)))
  483. {
  484. Application.OpenURL(UrlForum);
  485. }
  486. GUILayout.EndVertical();
  487. }
  488. #endregion
  489. private AccountService serviceClient;
  490. protected virtual void RegisterWithEmail(string email)
  491. {
  492. List<ServiceTypes> types = new List<ServiceTypes>();
  493. types.Add(ServiceTypes.Pun);
  494. if (PhotonEditorUtils.HasChat)
  495. {
  496. types.Add(ServiceTypes.Chat);
  497. }
  498. if (PhotonEditorUtils.HasVoice)
  499. {
  500. types.Add(ServiceTypes.Voice);
  501. }
  502. if (this.serviceClient == null)
  503. {
  504. this.serviceClient = new AccountService();
  505. this.serviceClient.CustomToken = CustomToken;
  506. this.serviceClient.CustomContext = CustomContext;
  507. }
  508. else
  509. {
  510. // while RegisterByEmail will check RequestPendingResult below, it would also display an error message. no needed in this case
  511. if (this.serviceClient.RequestPendingResult)
  512. {
  513. Debug.LogWarning("Registration request is pending a response. Please wait.");
  514. return;
  515. }
  516. }
  517. this.emailSentToAccount = email;
  518. this.emailSentToAccountIsRegistered = false;
  519. if (this.serviceClient.RegisterByEmail(email, types, RegisterWithEmailSuccessCallback, RegisterWithEmailErrorCallback, "PUN"+PhotonNetwork.PunVersion))
  520. {
  521. this.photonSetupState = PhotonSetupStates.EmailRegistrationPending;
  522. EditorUtility.DisplayProgressBar(CurrentLang.ConnectionTitle, CurrentLang.ConnectionInfo, 0.5f);
  523. }
  524. else
  525. {
  526. this.DisplayErrorMessage("Email registration request could not be sent. Retry again or check error logs and contact support.");
  527. }
  528. }
  529. private void RegisterWithEmailSuccessCallback(AccountServiceResponse res)
  530. {
  531. EditorUtility.ClearProgressBar();
  532. this.emailSentToAccountIsRegistered = true; // email is either registered now, or was already
  533. if (res.ReturnCode == AccountServiceReturnCodes.Success)
  534. {
  535. string key = ((int) ServiceTypes.Pun).ToString();
  536. string appId;
  537. if (res.ApplicationIds.TryGetValue(key, out appId))
  538. {
  539. this.mailOrAppId = appId;
  540. PhotonNetwork.PhotonServerSettings.UseCloud(this.mailOrAppId, null);
  541. key = ((int) ServiceTypes.Chat).ToString();
  542. if (res.ApplicationIds.TryGetValue(key, out appId))
  543. {
  544. PhotonNetwork.PhotonServerSettings.AppSettings.AppIdChat = appId;
  545. }
  546. else if (PhotonEditorUtils.HasChat)
  547. {
  548. Debug.LogWarning("Registration successful but no Chat AppId returned");
  549. }
  550. key = ((int) ServiceTypes.Voice).ToString();
  551. if (res.ApplicationIds.TryGetValue(key, out appId))
  552. {
  553. PhotonNetwork.PhotonServerSettings.AppSettings.AppIdVoice = appId;
  554. }
  555. else if (PhotonEditorUtils.HasVoice)
  556. {
  557. Debug.LogWarning("Registration successful but no Voice AppId returned");
  558. }
  559. PhotonEditor.SaveSettings();
  560. this.photonSetupState = PhotonSetupStates.GoEditPhotonServerSettings;
  561. }
  562. else
  563. {
  564. DisplayErrorMessage("Registration successful but no PUN AppId returned");
  565. }
  566. }
  567. else
  568. {
  569. PhotonEditor.SaveSettings();
  570. if (res.ReturnCode == AccountServiceReturnCodes.EmailAlreadyRegistered)
  571. {
  572. this.photonSetupState = PhotonSetupStates.EmailAlreadyRegistered;
  573. }
  574. else
  575. {
  576. DisplayErrorMessage(res.Message);
  577. }
  578. }
  579. }
  580. private void RegisterWithEmailErrorCallback(string error)
  581. {
  582. EditorUtility.ClearProgressBar();
  583. DisplayErrorMessage(error);
  584. }
  585. private void DisplayErrorMessage(string error)
  586. {
  587. EditorUtility.DisplayDialog(CurrentLang.ErrorTextTitle, error, CurrentLang.OkButton);
  588. this.photonSetupState = PhotonSetupStates.RegisterForPhotonCloud;
  589. }
  590. // Pings PhotonServerSettings and makes it selected (show in Inspector)
  591. private static void HighlightSettings()
  592. {
  593. ServerSettings serverSettings = (ServerSettings)Resources.Load(PhotonNetwork.ServerSettingsFileName, typeof(ServerSettings));
  594. Selection.objects = new UnityEngine.Object[] { serverSettings };
  595. EditorGUIUtility.PingObject(serverSettings);
  596. }
  597. // Marks settings object as dirty, so it gets saved.
  598. // unity 5.3 changes the usecase for SetDirty(). but here we don't modify a scene object! so it's ok to use
  599. private static void SaveSettings()
  600. {
  601. EditorUtility.SetDirty(PhotonNetwork.PhotonServerSettings);
  602. }
  603. #region RPC List Handling
  604. public static void UpdateRpcList()
  605. {
  606. //Debug.Log("UpdateRpcList()");
  607. if (PhotonNetwork.PhotonServerSettings == null)
  608. {
  609. Debug.LogWarning("UpdateRpcList() wasn not able to access the PhotonServerSettings. Not updating the RPCs.");
  610. return;
  611. }
  612. // check all "script assemblies" for methods with PunRPC attribute
  613. List<string> additionalRpcs = new List<string>(); // not yet listed rpc-method names go here
  614. List<string> allRpcs = new List<string>();
  615. #if UNITY_2019_2_OR_NEWER
  616. // we can make use of the new TypeCache to find methods with PunRPC attribute
  617. var extractedMethods = TypeCache.GetMethodsWithAttribute<PunRPC>();
  618. foreach (var methodInfo in extractedMethods)
  619. {
  620. allRpcs.Add(methodInfo.Name);
  621. if (!PhotonNetwork.PhotonServerSettings.RpcList.Contains(methodInfo.Name) && !additionalRpcs.Contains(methodInfo.Name))
  622. {
  623. additionalRpcs.Add(methodInfo.Name);
  624. }
  625. }
  626. #else
  627. System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => !(a.ManifestModule is System.Reflection.Emit.ModuleBuilder)).ToArray();
  628. foreach (var assembly in assemblies)
  629. {
  630. if (!assembly.Location.Contains("ScriptAssemblies") || assembly.FullName.StartsWith("Assembly-CSharp-Editor"))
  631. {
  632. continue;
  633. }
  634. var types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(MonoBehaviour)));
  635. var methodInfos = types.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
  636. var methodNames = methodInfos.Where(m => m.IsDefined(typeof(PunRPC), false)).Select(mi => mi.Name).ToArray();
  637. var additional = methodNames.Where(n => !PhotonNetwork.PhotonServerSettings.RpcList.Contains(n) && !additionalRpcs.Contains(n));
  638. allRpcs.AddRange(methodNames);
  639. additionalRpcs.AddRange(additional);
  640. }
  641. #endif
  642. if (additionalRpcs.Count <= 0)
  643. {
  644. //Debug.Log("UpdateRPCs did not found new.");
  645. return;
  646. }
  647. if (additionalRpcs.Count + PhotonNetwork.PhotonServerSettings.RpcList.Count >= byte.MaxValue)
  648. {
  649. if (allRpcs.Count <= byte.MaxValue)
  650. {
  651. bool clearList = EditorUtility.DisplayDialog(CurrentLang.IncorrectRPCListTitle, CurrentLang.IncorrectRPCListLabel, CurrentLang.RemoveOutdatedRPCsLabel, CurrentLang.CancelButton);
  652. if (clearList)
  653. {
  654. PhotonNetwork.PhotonServerSettings.RpcList.Clear();
  655. additionalRpcs = allRpcs.Distinct().ToList(); // we add all unique names
  656. }
  657. else
  658. {
  659. return;
  660. }
  661. }
  662. else
  663. {
  664. EditorUtility.DisplayDialog(CurrentLang.FullRPCListTitle, CurrentLang.FullRPCListLabel, CurrentLang.SkipRPCListUpdateLabel);
  665. return;
  666. }
  667. }
  668. additionalRpcs.Sort();
  669. Undo.RecordObject(PhotonNetwork.PhotonServerSettings, "RPC-list update of PUN.");
  670. PhotonNetwork.PhotonServerSettings.RpcList.AddRange(additionalRpcs);
  671. EditorUtility.SetDirty(PhotonNetwork.PhotonServerSettings);
  672. //Debug.Log("Updated RPCs. Added: "+additionalRpcs.Count);
  673. }
  674. public static void ClearRpcList()
  675. {
  676. bool clearList = EditorUtility.DisplayDialog(CurrentLang.PUNNameReplaceTitle, CurrentLang.PUNNameReplaceLabel, CurrentLang.RPCListCleared, CurrentLang.CancelButton);
  677. if (clearList)
  678. {
  679. ServerSettings serverSettings = PhotonNetwork.PhotonServerSettings;
  680. Undo.RecordObject(serverSettings, "RPC-list cleared for PUN.");
  681. serverSettings.RpcList.Clear();
  682. EditorUtility.SetDirty(serverSettings);
  683. Debug.LogWarning(CurrentLang.ServerSettingsCleanedWarning);
  684. }
  685. }
  686. #endregion
  687. }
  688. }