Prompts.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR)
  2. using System;
  3. using System.Collections;
  4. using System.Threading.Tasks;
  5. using UnityEngine;
  6. using UnityEngine.EventSystems;
  7. #if ENABLE_INPUT_SYSTEM
  8. using UnityEngine.InputSystem;
  9. #endif
  10. namespace SingularityGroup.HotReload {
  11. internal class Prompts : MonoBehaviour {
  12. public GameObject retryPrompt;
  13. public GameObject connectedPrompt;
  14. public GameObject questionPrompt;
  15. [Header("Other")]
  16. [Tooltip("Used when project does not create an EventSystem early enough")]
  17. public GameObject fallbackEventSystem;
  18. #region Singleton
  19. private static Prompts _I;
  20. /// <summary>
  21. /// All usages must check that <see cref="PlayerEntrypoint.RuntimeSupportsHotReload"/> is true before accessing this singleton.
  22. /// </summary>
  23. /// <remarks>
  24. /// This getter can throw on unsupported platforms (HotReloadSettingsObject resource doesn't exist on unsupported platforms).
  25. /// </remarks>
  26. public static Prompts I {
  27. get {
  28. if (_I == null) {
  29. // allow showing prompts in editor (for testing)
  30. if (!Application.isEditor && !PlayerEntrypoint.IsPlayerWithHotReload()) {
  31. throw new NotSupportedException("IsPlayerWithHotReload() is false");
  32. }
  33. var go = Instantiate(HotReloadSettingsObject.I.PromptsPrefab,
  34. new Vector3(0, 0, 0), Quaternion.identity);
  35. go.name = nameof(Prompts) + "_singleton";
  36. if (Application.isPlaying) {
  37. DontDestroyOnLoad(go);
  38. }
  39. _I = go.GetComponentInChildren<Prompts>();
  40. }
  41. return _I;
  42. }
  43. }
  44. #endregion
  45. /// <seealso cref="ShowConnectionDialog"/>
  46. public static void SetConnectionState(string state, bool log = true) {
  47. var connectionDialog = I.connectedPrompt.GetComponentInChildren<ConnectionDialog>();
  48. if (log) Log.Debug($"SetConnectionState( {state} )");
  49. if (connectionDialog) {
  50. connectionDialog.SetSummary(state);
  51. }
  52. }
  53. /// <seealso cref="SetConnectionState"/>
  54. public static void ShowConnectionDialog() {
  55. I.retryPrompt.SetActive(false);
  56. I.connectedPrompt.SetActive(true);
  57. }
  58. public static async Task<bool> ShowQuestionDialog(QuestionDialog.Config config) {
  59. var tcs = new TaskCompletionSource<bool>();
  60. var holder = I.questionPrompt;
  61. var dialog = holder.GetComponentInChildren<QuestionDialog>();
  62. dialog.completion = tcs;
  63. dialog.UpdateView(config);
  64. holder.SetActive(true);
  65. return await tcs.Task;
  66. }
  67. public static void ShowRetryDialog(
  68. PatchServerInfo patchServerInfo,
  69. ServerHandshake.Result handshakeResults = ServerHandshake.Result.None,
  70. bool auto = true
  71. ) {
  72. var retryDialog = I.retryPrompt.GetComponentInChildren<RetryDialog>();
  73. RetryDialog.TargetServer = patchServerInfo;
  74. RetryDialog.HandshakeResults = handshakeResults;
  75. if (patchServerInfo == null) {
  76. retryDialog.DebugInfo = $"patchServerInfo == null {handshakeResults}";
  77. } else {
  78. retryDialog.DebugInfo = $"{RequestHelper.CreateUrl(patchServerInfo)} {handshakeResults}";
  79. }
  80. retryDialog.autoConnect = auto;
  81. I.connectedPrompt.SetActive(false);
  82. I.retryPrompt.SetActive(true);
  83. }
  84. #region fallback event system
  85. private void Start() {
  86. StartCoroutine(DelayedEnsureEventSystem());
  87. }
  88. private bool userTriedToInteract = false;
  89. private void Update() {
  90. if (!userTriedToInteract) {
  91. // when user interacts with the screen, make sure overlay can handle taps
  92. #if ENABLE_INPUT_SYSTEM
  93. if ((Touchscreen.current != null && Touchscreen.current.touches.Count > 0) ||
  94. (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)) {
  95. userTriedToInteract = true;
  96. DoEnsureEventSystem();
  97. }
  98. #else
  99. if (Input.touchCount > 0 || Input.GetMouseButtonDown(0)) {
  100. userTriedToInteract = true;
  101. DoEnsureEventSystem();
  102. }
  103. #endif
  104. }
  105. }
  106. private IEnumerator DelayedEnsureEventSystem() {
  107. // allow some delay in-case the project loads the EventSystem asynchronously (perhaps in a second scene)
  108. if (EventSystem.current == null) {
  109. yield return new WaitForSeconds(1f);
  110. DoEnsureEventSystem();
  111. }
  112. }
  113. /// Scene must contain an EventSystem and StandaloneInputModule, otherwise clicking/tapping on the overlay does nothing.
  114. private void DoEnsureEventSystem() {
  115. if (EventSystem.current == null) {
  116. Log.Info($"No EventSystem is active, enabling an EventSystem inside Hot Reload {name} prefab." +
  117. " A Unity EventSystem and an Input module is required for tapping buttons on the Unity UI.");
  118. fallbackEventSystem.SetActive(true);
  119. }
  120. }
  121. #endregion
  122. }
  123. }
  124. #endif