EditorIndicationState.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using SingularityGroup.HotReload.DTO;
  5. namespace SingularityGroup.HotReload.Editor {
  6. internal static class EditorIndicationState {
  7. internal enum IndicationStatus {
  8. Stopped,
  9. Started,
  10. Stopping,
  11. Installing,
  12. Starting,
  13. Reloaded,
  14. PartiallySupported,
  15. Unsupported,
  16. Patching,
  17. Loading,
  18. Compiling,
  19. CompileErrors,
  20. ActivationFailed,
  21. FinishRegistration,
  22. Undetected,
  23. }
  24. internal static readonly string greyIconPath = "grey";
  25. internal static readonly string greenIconPath = "green";
  26. internal static readonly string redIconPath = "red";
  27. private static readonly Dictionary<IndicationStatus, string> IndicationIcon = new Dictionary<IndicationStatus, string> {
  28. // grey icon:
  29. { IndicationStatus.FinishRegistration, greyIconPath },
  30. { IndicationStatus.Stopped, greyIconPath },
  31. // green icon:
  32. { IndicationStatus.Started, greenIconPath },
  33. // log icons:
  34. { IndicationStatus.Reloaded, HotReloadTimelineHelper.alertIconString[AlertType.AppliedChange] },
  35. { IndicationStatus.Unsupported, HotReloadTimelineHelper.alertIconString[AlertType.UnsupportedChange] },
  36. { IndicationStatus.Undetected, HotReloadTimelineHelper.alertIconString[AlertType.UndetectedChange] },
  37. { IndicationStatus.PartiallySupported, HotReloadTimelineHelper.alertIconString[AlertType.PartiallySupportedChange] },
  38. { IndicationStatus.CompileErrors, HotReloadTimelineHelper.alertIconString[AlertType.CompileError] },
  39. // spinner:
  40. { IndicationStatus.Stopping, Spinner.SpinnerIconPath },
  41. { IndicationStatus.Starting, Spinner.SpinnerIconPath },
  42. { IndicationStatus.Patching, Spinner.SpinnerIconPath },
  43. { IndicationStatus.Loading, Spinner.SpinnerIconPath },
  44. { IndicationStatus.Compiling, Spinner.SpinnerIconPath },
  45. { IndicationStatus.Installing, Spinner.SpinnerIconPath },
  46. // red icon:
  47. { IndicationStatus.ActivationFailed, redIconPath },
  48. };
  49. private static readonly IndicationStatus[] SpinnerIndications = IndicationIcon
  50. .Where(kvp => kvp.Value == Spinner.SpinnerIconPath)
  51. .Select(kvp => kvp.Key)
  52. .ToArray();
  53. // NOTE: if you add longer text, make sure UI is wide enough for it
  54. public static readonly Dictionary<IndicationStatus, string> IndicationText = new Dictionary<IndicationStatus, string> {
  55. { IndicationStatus.FinishRegistration, "Finish Registration" },
  56. { IndicationStatus.Started, "Waiting for code changes" },
  57. { IndicationStatus.Stopping, "Stopping Hot Reload" },
  58. { IndicationStatus.Stopped, "Hot Reload inactive" },
  59. { IndicationStatus.Installing, "Installing" },
  60. { IndicationStatus.Starting, "Starting Hot Reload" },
  61. { IndicationStatus.Reloaded, "Reload finished" },
  62. { IndicationStatus.PartiallySupported, "Changes partially applied" },
  63. { IndicationStatus.Unsupported, "Finished with warnings" },
  64. { IndicationStatus.Patching, "Reloading" },
  65. { IndicationStatus.Compiling, "Compiling" },
  66. { IndicationStatus.CompileErrors, "Scripts have compile errors" },
  67. { IndicationStatus.ActivationFailed, "Activation failed" },
  68. { IndicationStatus.Loading, "Loading" },
  69. { IndicationStatus.Undetected, "No changes applied"},
  70. };
  71. private const int MinSpinnerDuration = 200;
  72. private static DateTime spinnerStartedAt;
  73. private static IndicationStatus latestStatus;
  74. private static bool SpinnerCompletedMinDuration => DateTime.UtcNow - spinnerStartedAt > TimeSpan.FromMilliseconds(MinSpinnerDuration);
  75. private static IndicationStatus GetIndicationStatus() {
  76. var status = GetIndicationStatusCore();
  77. // Note: performance sensitive code, don't use Link
  78. bool newStatusIsSpinner = false;
  79. for (var i = 0; i < SpinnerIndications.Length; i++) {
  80. if (SpinnerIndications[i] == status) {
  81. newStatusIsSpinner = true;
  82. }
  83. }
  84. bool latestStatusIsSpinner = false;
  85. for (var i = 0; i < SpinnerIndications.Length; i++) {
  86. if (SpinnerIndications[i] == latestStatus) {
  87. newStatusIsSpinner = true;
  88. }
  89. }
  90. if (status == latestStatus) {
  91. return status;
  92. } else if (latestStatusIsSpinner) {
  93. if (newStatusIsSpinner) {
  94. return status;
  95. } else if (SpinnerCompletedMinDuration) {
  96. latestStatus = status;
  97. return status;
  98. } else {
  99. return latestStatus;
  100. }
  101. } else if (newStatusIsSpinner) {
  102. spinnerStartedAt = DateTime.UtcNow;
  103. latestStatus = status;
  104. return status;
  105. } else {
  106. spinnerStartedAt = DateTime.UtcNow;
  107. latestStatus = IndicationStatus.Loading;
  108. return status;
  109. }
  110. }
  111. private static IndicationStatus GetIndicationStatusCore() {
  112. if (RedeemLicenseHelper.I.RegistrationRequired)
  113. return IndicationStatus.FinishRegistration;
  114. if (EditorCodePatcher.DownloadRequired && EditorCodePatcher.DownloadStarted || EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Starting && !EditorCodePatcher.Stopping)
  115. return IndicationStatus.Installing;
  116. if (EditorCodePatcher.Stopping)
  117. return IndicationStatus.Stopping;
  118. if (EditorCodePatcher.Compiling && !EditorCodePatcher.Stopping && !EditorCodePatcher.Starting && EditorCodePatcher.Running)
  119. return IndicationStatus.Compiling;
  120. if (EditorCodePatcher.Starting && !EditorCodePatcher.Stopping)
  121. return IndicationStatus.Starting;
  122. if (!EditorCodePatcher.Running)
  123. return IndicationStatus.Stopped;
  124. if (EditorCodePatcher.Status?.isLicensed != true && EditorCodePatcher.Status?.isFree != true && EditorCodePatcher.Status?.freeSessionFinished == true)
  125. return IndicationStatus.ActivationFailed;
  126. if (EditorCodePatcher.compileError)
  127. return IndicationStatus.CompileErrors;
  128. // fallback on patch status
  129. if (!EditorCodePatcher.Started && !EditorCodePatcher.Running) {
  130. return IndicationStatus.Stopped;
  131. }
  132. switch (EditorCodePatcher.patchStatus) {
  133. case PatchStatus.Idle:
  134. if (!EditorCodePatcher.Compiling && !EditorCodePatcher.firstPatchAttempted && !EditorCodePatcher.compileError) {
  135. return IndicationStatus.Started;
  136. }
  137. if (EditorCodePatcher._applyingFailed) {
  138. return IndicationStatus.Unsupported;
  139. }
  140. if (EditorCodePatcher._appliedPartially) {
  141. return IndicationStatus.PartiallySupported;
  142. }
  143. if (EditorCodePatcher._appliedUndetected) {
  144. return IndicationStatus.Undetected;
  145. }
  146. return IndicationStatus.Reloaded;
  147. case PatchStatus.Patching: return IndicationStatus.Patching;
  148. case PatchStatus.Unsupported: return IndicationStatus.Unsupported;
  149. case PatchStatus.Compiling: return IndicationStatus.Compiling;
  150. case PatchStatus.CompileError: return IndicationStatus.CompileErrors;
  151. case PatchStatus.None:
  152. default: return IndicationStatus.Reloaded;
  153. }
  154. }
  155. internal static IndicationStatus CurrentIndicationStatus => GetIndicationStatus();
  156. internal static bool SpinnerActive => SpinnerIndications.Contains(CurrentIndicationStatus);
  157. internal static string IndicationIconPath => IndicationIcon[CurrentIndicationStatus];
  158. internal static string IndicationStatusText {
  159. get {
  160. var indicationStatus = CurrentIndicationStatus;
  161. string txt;
  162. if (indicationStatus == IndicationStatus.Starting && EditorCodePatcher.StartupProgress != null) {
  163. txt = EditorCodePatcher.StartupProgress.Item2;
  164. } else if (!IndicationText.TryGetValue(indicationStatus, out txt)) {
  165. Log.Warning($"Indication text not found for status {indicationStatus}");
  166. } else {
  167. txt = IndicationText[indicationStatus];
  168. }
  169. return txt;
  170. }
  171. }
  172. }
  173. }