| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using SingularityGroup.HotReload.DTO;
- using SingularityGroup.HotReload.EditorDependencies;
- using UnityEditor;
- using UnityEditor.Compilation;
- using UnityEngine;
- using Color = UnityEngine.Color;
- using Task = System.Threading.Tasks.Task;
- #if UNITY_2019_4_OR_NEWER
- using Unity.CodeEditor;
- #endif
- namespace SingularityGroup.HotReload.Editor {
- internal class ErrorData {
- public string fileName;
- public string error;
- public TextAsset file;
- public int lineNumber;
- public string stacktrace;
- public string linkString;
- private static string[] supportedPaths = new[] { Path.GetFullPath("Assets"), Path.GetFullPath("Plugins") };
-
- public static ErrorData GetErrorData(string errorString) {
- // Get the relevant file name
- string stackTrace = errorString;
- string fileName = null;
- try {
- int csIndex = 0;
- int attempt = 0;
- do {
- csIndex = errorString.IndexOf(".cs", csIndex + 1, StringComparison.Ordinal);
- if (csIndex == -1) {
- break;
- }
- int fileNameStartIndex = csIndex - 1;
- for (; fileNameStartIndex >= 0; fileNameStartIndex--) {
- if (!char.IsLetter(errorString[fileNameStartIndex])) {
- if (errorString.Contains("error CS")) {
- fileName = errorString.Substring(fileNameStartIndex + 1,
- csIndex - fileNameStartIndex + ".cs".Length - 1);
- } else {
- fileName = errorString.Substring(fileNameStartIndex,
- csIndex - fileNameStartIndex + ".cs".Length);
- }
- break;
- }
- }
- } while (attempt++ < 100 && fileName == null);
- } catch {
- // ignore
- }
- fileName = fileName ?? "Tap to show stacktrace";
-
- // Get the error
- string error = (errorString.Contains("error CS")
- ? "Compile error, "
- : "Unsupported change detected, ") + "tap here to see more.";
- int endOfError = errorString.IndexOf(". in ", StringComparison.Ordinal);
- string specialChars = "\"'/\\";
- char[] characters = specialChars.ToCharArray();
- int specialChar = errorString.IndexOfAny(characters);
- try {
- if (errorString.Contains("error CS") ) {
- error = errorString.Substring(errorString.IndexOf("error CS", StringComparison.Ordinal), errorString.Length - errorString.IndexOf("error CS", StringComparison.Ordinal)).Trim();
- using (StringReader reader = new StringReader(error)) {
- string line;
- while ((line = reader.ReadLine()) != null) {
- error = line;
- break;
- }
- }
- } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && endOfError > 0) {
- error = errorString.Substring("errors: ".Length, endOfError - "errors: ".Length).Trim();
- } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && specialChar > 0) {
- error = errorString.Substring("errors: ".Length, specialChar - "errors: ".Length).Trim();
- }
- } catch {
- // ignore
- }
- // Get relative path
- TextAsset file = null;
- try {
- foreach (var path in supportedPaths) {
- int lastprojectIndex = 0;
- int attempt = 0;
- while (attempt++ < 100 && !file) {
- lastprojectIndex = errorString.IndexOf(path, lastprojectIndex + 1, StringComparison.Ordinal);
- if (lastprojectIndex == -1) {
- break;
- }
- var fullCsIndex = errorString.IndexOf(".cs", lastprojectIndex, StringComparison.Ordinal);
- var l = fullCsIndex - lastprojectIndex + ".cs".Length;
- if (l <= 0) {
- continue;
- }
- var candidateAbsolutePath = errorString.Substring(lastprojectIndex, fullCsIndex - lastprojectIndex + ".cs".Length);
- var candidateRelativePath = EditorCodePatcher.GetRelativePath(filespec: candidateAbsolutePath, folder: path);
- file = AssetDatabase.LoadAssetAtPath<TextAsset>(candidateRelativePath);
- }
- }
- } catch {
- // ignore
- }
-
- // Get the line number
- int lineNumber = 0;
- try {
- int lastIndex = 0;
- int attempt = 0;
- do {
- lastIndex = errorString.IndexOf(fileName, lastIndex + 1, StringComparison.Ordinal);
- if (lastIndex == -1) {
- break;
- }
- var part = errorString.Substring(lastIndex + fileName.Length);
- if (!part.StartsWith(errorString.Contains("error CS") ? "(" : ":", StringComparison.Ordinal)
- || part.Length == 1
- || !char.IsDigit(part[1])
- ) {
- continue;
- }
- int y = 1;
- for (; y < part.Length; y++) {
- if (!char.IsDigit(part[y])) {
- break;
- }
- }
- if (int.TryParse(part.Substring(1, errorString.Contains("error CS") ? y - 1 : y), out lineNumber)) {
- break;
- }
- } while (attempt++ < 100);
- } catch {
- //ignore
- }
- return new ErrorData() {
- fileName = fileName,
- error = error,
- file = file,
- lineNumber = lineNumber,
- stacktrace = stackTrace,
- linkString = lineNumber > 0 ? fileName + ":" + lineNumber : fileName
- };
- }
-
- }
-
- internal struct HotReloadRunTabState {
- public readonly bool spinnerActive;
- public readonly string indicationIconPath;
- public readonly bool requestingDownloadAndRun;
- public readonly bool starting;
- public readonly bool stopping;
- public readonly bool running;
- public readonly Tuple<float, string> startupProgress;
- public readonly string indicationStatusText;
- public readonly LoginStatusResponse loginStatus;
- public readonly bool downloadRequired;
- public readonly bool downloadStarted;
- public readonly bool requestingLoginInfo;
- public readonly RedeemStage redeemStage;
- public readonly int suggestionCount;
- public HotReloadRunTabState(
- bool spinnerActive,
- string indicationIconPath,
- bool requestingDownloadAndRun,
- bool starting,
- bool stopping,
- bool running,
- Tuple<float, string> startupProgress,
- string indicationStatusText,
- LoginStatusResponse loginStatus,
- bool downloadRequired,
- bool downloadStarted,
- bool requestingLoginInfo,
- RedeemStage redeemStage,
- int suggestionCount
- ) {
- this.spinnerActive = spinnerActive;
- this.indicationIconPath = indicationIconPath;
- this.requestingDownloadAndRun = requestingDownloadAndRun;
- this.starting = starting;
- this.stopping = stopping;
- this.running = running;
- this.startupProgress = startupProgress;
- this.indicationStatusText = indicationStatusText;
- this.loginStatus = loginStatus;
- this.downloadRequired = downloadRequired;
- this.downloadStarted = downloadStarted;
- this.requestingLoginInfo = requestingLoginInfo;
- this.redeemStage = redeemStage;
- this.suggestionCount = suggestionCount;
- }
- public static HotReloadRunTabState Current => new HotReloadRunTabState(
- spinnerActive: EditorIndicationState.SpinnerActive,
- indicationIconPath: EditorIndicationState.IndicationIconPath,
- requestingDownloadAndRun: EditorCodePatcher.RequestingDownloadAndRun,
- starting: EditorCodePatcher.Starting,
- stopping: EditorCodePatcher.Stopping,
- running: EditorCodePatcher.Running,
- startupProgress: EditorCodePatcher.StartupProgress,
- indicationStatusText: EditorIndicationState.IndicationStatusText,
- loginStatus: EditorCodePatcher.Status,
- downloadRequired: EditorCodePatcher.DownloadRequired,
- downloadStarted: EditorCodePatcher.DownloadStarted,
- requestingLoginInfo: EditorCodePatcher.RequestingLoginInfo,
- redeemStage: RedeemLicenseHelper.I.RedeemStage,
- suggestionCount: HotReloadTimelineHelper.Suggestions.Count
- );
- }
- internal struct LicenseErrorData {
- public readonly string description;
- public bool showBuyButton;
- public string buyButtonText;
- public readonly bool showLoginButton;
- public readonly string loginButtonText;
- public readonly bool showSupportButton;
- public readonly string supportButtonText;
- public readonly bool showManageLicenseButton;
- public readonly string manageLicenseButtonText;
- public LicenseErrorData(string description, bool showManageLicenseButton = false, string manageLicenseButtonText = "", string loginButtonText = "", bool showSupportButton = false, string supportButtonText = "", bool showBuyButton = false, string buyButtonText = "", bool showLoginButton = false) {
- this.description = description;
- this.showManageLicenseButton = showManageLicenseButton;
- this.manageLicenseButtonText = manageLicenseButtonText;
- this.loginButtonText = loginButtonText;
- this.showSupportButton = showSupportButton;
- this.supportButtonText = supportButtonText;
- this.showBuyButton = showBuyButton;
- this.buyButtonText = buyButtonText;
- this.showLoginButton = showLoginButton;
- }
- }
-
- internal class HotReloadRunTab : HotReloadTabBase {
- private static string _pendingEmail;
- private static string _pendingPassword;
- private string _pendingPromoCode;
- private bool _requestingActivatePromoCode;
- private static Tuple<string, MessageType> _activateInfoMessage;
- private HotReloadRunTabState currentState => _window.RunTabState;
- // Has Indie or Pro license (even if not currenctly active)
- public bool HasPayedLicense => currentState.loginStatus != null && (currentState.loginStatus.isIndieLicense || currentState.loginStatus.isBusinessLicense);
- public bool TrialLicense => currentState.loginStatus != null && (currentState.loginStatus?.isTrial == true);
-
- private Vector2 _patchedMethodsScrollPos;
- private Vector2 _runTabScrollPos;
- private string promoCodeError;
- private MessageType promoCodeErrorType;
- private bool promoCodeActivatedThisSession;
-
- public HotReloadRunTab(HotReloadWindow window) : base(window, "Run", "forward", "Run and monitor the current Hot Reload session.") { }
- public override void OnGUI() {
- using(new EditorGUILayout.VerticalScope()) {
- OnGUICore();
- }
- }
- internal static bool ShouldRenderConsumption(HotReloadRunTabState currentState) => (currentState.running && !currentState.starting && !currentState.stopping && currentState.loginStatus?.isLicensed != true && currentState.loginStatus?.isFree != true && !EditorCodePatcher.LoginNotRequired) && !(currentState.loginStatus == null || currentState.loginStatus.isFree);
-
- void OnGUICore() {
- using (var scope = new EditorGUILayout.ScrollViewScope(_runTabScrollPos, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(Math.Max(HotReloadWindowStyles.windowScreenHeight, 800)), GUILayout.MaxWidth(Math.Max(HotReloadWindowStyles.windowScreenWidth, 800)))) {
- _runTabScrollPos.x = scope.scrollPosition.x;
- _runTabScrollPos.y = scope.scrollPosition.y;
- using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.DynamiSection)) {
- if (HotReloadWindowStyles.windowScreenWidth > Constants.UpgradeLicenseNoteHideWidth
- && HotReloadWindowStyles.windowScreenHeight > Constants.UpgradeLicenseNoteHideHeight
- ) {
- RenderUpgradeLicenseNote(currentState, HotReloadWindowStyles.UpgradeLicenseButtonStyle);
- }
- RenderIndicationPanel();
- if (CanRenderBars(currentState)) {
- RenderBars(currentState);
- // clear red dot next time button shows
- HotReloadState.ShowingRedDot = false;
- }
- }
- }
- // At the end to not fuck up rendering https://answers.unity.com/questions/400454/argumentexception-getting-control-0s-position-in-a-1.html
- var renderStart = !EditorCodePatcher.Running && !EditorCodePatcher.Starting && !currentState.requestingDownloadAndRun && currentState.redeemStage == RedeemStage.None;
- var e = Event.current;
- if (renderStart && e.type == EventType.KeyUp
- && (e.keyCode == KeyCode.Return
- || e.keyCode == KeyCode.KeypadEnter)
- ) {
- EditorCodePatcher.DownloadAndRun().Forget();
- }
- }
- internal static void RenderUpgradeLicenseNote(HotReloadRunTabState currentState, GUIStyle style) {
- var isIndie = RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Indie
- || EditorCodePatcher.licenseType == UnityLicenseType.UnityPersonalPlus;
- if (RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Business
- && currentState.loginStatus?.isBusinessLicense != true
- && EditorCodePatcher.Running
- && (PackageConst.IsAssetStoreBuild || HotReloadPrefs.RateAppShown)
- ) {
- // Warn asset store users they need to buy a business license
- // Website users get reminded after using Hot Reload for 5+ days
- RenderBusinessLicenseInfo(style);
- } else if (isIndie
- && HotReloadPrefs.RateAppShown
- && !PackageConst.IsAssetStoreBuild
- && EditorCodePatcher.Running
- && currentState.loginStatus?.isBusinessLicense != true
- && currentState.loginStatus?.isIndieLicense != true
- ) {
- // Reminder users they need to buy an indie license
- RenderIndieLicenseInfo(style);
- }
- }
-
- internal static bool CanRenderBars(HotReloadRunTabState currentState) {
- return HotReloadWindowStyles.windowScreenHeight > Constants.EventsListHideHeight
- && HotReloadWindowStyles.windowScreenWidth > Constants.EventsListHideWidth
- && !currentState.starting
- && !currentState.stopping
- && !currentState.requestingDownloadAndRun
- ;
- }
-
- static Texture2D GetFoldoutIcon(AlertEntry alertEntry) {
- InvertibleIcon alertIcon = InvertibleIcon.FoldoutClosed;
- if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) {
- alertIcon = InvertibleIcon.FoldoutOpen;
- }
- return GUIHelper.GetInvertibleIcon(alertIcon);
- }
-
- static void ToggleEntry(AlertEntry alertEntry) {
- if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) {
- HotReloadTimelineHelper.expandedEntries.Remove(alertEntry);
- } else {
- HotReloadTimelineHelper.expandedEntries.Add(alertEntry);
- }
- }
-
- static void RenderEntries(TimelineType timelineType) {
- List<AlertEntry> alertEntries;
-
- alertEntries = timelineType == TimelineType.Suggestions ? HotReloadTimelineHelper.Suggestions : HotReloadTimelineHelper.EventsTimeline;
- bool skipChildren = false;
- for (int i = 0; i < alertEntries.Count; i++) {
- var alertEntry = alertEntries[i];
- if (i > HotReloadTimelineHelper.maxVisibleEntries && alertEntry.entryType != EntryType.Child) {
- break;
- }
- if (timelineType != TimelineType.Suggestions) {
- if (alertEntry.entryType != EntryType.Child
- && !enabledFilters.Contains(alertEntry.alertType)
- ) {
- skipChildren = true;
- continue;
- } else if (alertEntry.entryType == EntryType.Child && skipChildren) {
- continue;
- } else {
- skipChildren = false;
- }
- }
-
- EntryType entryType = alertEntry.entryType;
- string title = $" {alertEntry.title}{(!string.IsNullOrEmpty(alertEntry.shortDescription) ? $": {alertEntry.shortDescription}": "")}";
- Texture2D icon = null;
- GUIStyle style;
- if (entryType != EntryType.Child) {
- icon = GUIHelper.GetLocalIcon(HotReloadTimelineHelper.alertIconString[alertEntry.iconType]);
- }
- if (entryType == EntryType.Child) {
- style = HotReloadWindowStyles.ChildBarStyle;
- } else if (entryType == EntryType.Foldout) {
- style = HotReloadWindowStyles.FoldoutBarStyle;
- } else {
- style = HotReloadWindowStyles.BarStyle;
- }
- Rect startRect;
- using (new EditorGUILayout.HorizontalScope()) {
- GUILayout.Space(0);
- Rect spaceRect = GUILayoutUtility.GetLastRect();
- // entry header foldout arrow
- if (entryType == EntryType.Foldout) {
- GUI.Label(new Rect(spaceRect.x + 3, spaceRect.y, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry)));
- } else if (entryType == EntryType.Child) {
- GUI.Label(new Rect(spaceRect.x + 26, spaceRect.y + 2, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry)));
- }
- // a workaround to limit the width of the label
- GUILayout.Label(new GUIContent(""), style);
- startRect = GUILayoutUtility.GetLastRect();
- GUI.Label(startRect, new GUIContent(title, icon), style);
- }
- bool clickableDescription = (alertEntry.title == "Unsupported change" || alertEntry.title == "Compile error" || alertEntry.title == "Failed applying patch to method") && alertEntry.alertData.alertEntryType != AlertEntryType.InlinedMethod;
-
- if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry) || alertEntry.alertType == AlertType.CompileError) {
- using (new EditorGUILayout.VerticalScope()) {
- using (new EditorGUILayout.HorizontalScope()) {
- using (new EditorGUILayout.VerticalScope(entryType == EntryType.Child ? HotReloadWindowStyles.ChildEntryBoxStyle : HotReloadWindowStyles.EntryBoxStyle)) {
- if (alertEntry.alertType == AlertType.Suggestion || !clickableDescription) {
- GUILayout.Label(alertEntry.description, HotReloadWindowStyles.LabelStyle);
- }
- if (alertEntry.actionData != null) {
- alertEntry.actionData.Invoke();
- }
- GUILayout.Space(5f);
- }
- }
- }
- }
-
- // remove button
- if (timelineType == TimelineType.Suggestions && alertEntry.hasExitButton) {
- var isClick = GUI.Button(new Rect(startRect.x + startRect.width - 20, startRect.y + 2, 20, 20), new GUIContent(GUIHelper.GetInvertibleIcon(InvertibleIcon.Close)), HotReloadWindowStyles.RemoveIconStyle);
- if (isClick) {
- HotReloadTimelineHelper.EventsTimeline.Remove(alertEntry);
- var kind = HotReloadSuggestionsHelper.FindSuggestionKind(alertEntry);
- if (kind != null) {
- HotReloadSuggestionsHelper.SetSuggestionInactive((HotReloadSuggestionKind)kind);
- }
- _instantRepaint = true;
- }
- }
- // Extend background to whole entry
- var endRect = GUILayoutUtility.GetLastRect();
- if (GUI.Button(new Rect(startRect) { height = endRect.y - startRect.y + endRect.height}, new GUIContent(""), HotReloadWindowStyles.BarBackgroundStyle) && (entryType == EntryType.Child || entryType == EntryType.Foldout)) {
- ToggleEntry(alertEntry);
- }
-
- if (alertEntry.alertType != AlertType.Suggestion && HotReloadWindowStyles.windowScreenWidth > 400 && entryType != EntryType.Child) {
- using (new EditorGUILayout.HorizontalScope()) {
- var ago = (DateTime.Now - alertEntry.timestamp);
- GUI.Label(new Rect(startRect.x + startRect.width - 60, startRect.y, 80, 20), ago.TotalMinutes < 1 ? "now" : $"{(ago.TotalHours > 1 ? $"{Math.Floor(ago.TotalHours)} h " : string.Empty)}{ago.Minutes} min", HotReloadWindowStyles.TimestampStyle);
- }
- }
-
- GUILayout.Space(1f);
- }
- if (timelineType != TimelineType.Suggestions && HotReloadTimelineHelper.GetRunTabTimelineEventCount() > 40) {
- GUILayout.Space(3f);
- GUILayout.Label(Constants.Only40EntriesShown, HotReloadWindowStyles.EmptyListText);
- }
- }
- private static List<AlertType> _enabledFilters;
- private static List<AlertType> enabledFilters {
- get {
- if (_enabledFilters == null) {
- _enabledFilters = new List<AlertType>();
- }
-
- if (HotReloadPrefs.RunTabUnsupportedChangesFilter && !_enabledFilters.Contains(AlertType.UnsupportedChange))
- _enabledFilters.Add(AlertType.UnsupportedChange);
- if (!HotReloadPrefs.RunTabUnsupportedChangesFilter && _enabledFilters.Contains(AlertType.UnsupportedChange))
- _enabledFilters.Remove(AlertType.UnsupportedChange);
-
- if (HotReloadPrefs.RunTabCompileErrorFilter && !_enabledFilters.Contains(AlertType.CompileError))
- _enabledFilters.Add(AlertType.CompileError);
- if (!HotReloadPrefs.RunTabCompileErrorFilter && _enabledFilters.Contains(AlertType.CompileError))
- _enabledFilters.Remove(AlertType.CompileError);
-
- if (HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter && !_enabledFilters.Contains(AlertType.PartiallySupportedChange))
- _enabledFilters.Add(AlertType.PartiallySupportedChange);
- if (!HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter && _enabledFilters.Contains(AlertType.PartiallySupportedChange))
- _enabledFilters.Remove(AlertType.PartiallySupportedChange);
-
- if (HotReloadPrefs.RunTabUndetectedPatchesFilter && !_enabledFilters.Contains(AlertType.UndetectedChange))
- _enabledFilters.Add(AlertType.UndetectedChange);
- if (!HotReloadPrefs.RunTabUndetectedPatchesFilter && _enabledFilters.Contains(AlertType.UndetectedChange))
- _enabledFilters.Remove(AlertType.UndetectedChange);
-
- if (HotReloadPrefs.RunTabAppliedPatchesFilter && !_enabledFilters.Contains(AlertType.AppliedChange))
- _enabledFilters.Add(AlertType.AppliedChange);
- if (!HotReloadPrefs.RunTabAppliedPatchesFilter && _enabledFilters.Contains(AlertType.AppliedChange))
- _enabledFilters.Remove(AlertType.AppliedChange);
-
- return _enabledFilters;
- }
- }
-
- private Vector2 suggestionsScroll;
- static GUILayoutOption[] timelineButtonOptions = new[] { GUILayout.Height(27), GUILayout.Width(100) };
- internal static void RenderBars(HotReloadRunTabState currentState) {
- if (currentState.suggestionCount > 0) {
- GUILayout.Space(5f);
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) {
- using (new EditorGUILayout.VerticalScope()) {
- HotReloadPrefs.RunTabEventsSuggestionsFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsSuggestionsFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle);
- GUILayout.Space(-23);
- if (GUILayout.Button($"Suggestions ({currentState.suggestionCount.ToString()})", HotReloadWindowStyles.ClickableLabelBoldStyle, GUILayout.Height(27))) {
- HotReloadPrefs.RunTabEventsSuggestionsFoldout = !HotReloadPrefs.RunTabEventsSuggestionsFoldout;
- }
- if (HotReloadPrefs.RunTabEventsSuggestionsFoldout) {
- using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Scroll)) {
- RenderEntries(TimelineType.Suggestions);
- }
- }
- }
- }
- }
- GUILayout.Space(5f);
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) {
- using (new EditorGUILayout.VerticalScope()) {
- HotReloadPrefs.RunTabEventsTimelineFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsTimelineFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle);
- GUILayout.Space(-23);
- if (GUILayout.Button("Timeline", HotReloadWindowStyles.ClickableLabelBoldStyle, timelineButtonOptions)) {
- HotReloadPrefs.RunTabEventsTimelineFoldout = !HotReloadPrefs.RunTabEventsTimelineFoldout;
- }
- if (HotReloadPrefs.RunTabEventsTimelineFoldout) {
- GUILayout.Space(-10);
- var noteShown = HotReloadTimelineHelper.GetRunTabTimelineEventCount() == 0 || !currentState.running;
- using (new EditorGUILayout.HorizontalScope()) {
- if (noteShown) {
- GUILayout.Space(2f);
- using (new EditorGUILayout.VerticalScope()) {
- GUILayout.Space(2f);
- string text;
- if (currentState.redeemStage != RedeemStage.None) {
- text = "Complete registration before using Hot Reload";
- } else if (!currentState.running) {
- text = "Use the Start button to activate Hot Reload";
- } else if (enabledFilters.Count < 4 && HotReloadTimelineHelper.EventsTimeline.Count != 0) {
- text = "Enable filters to see events";
- } else {
- text = "Make code changes to see events";
- }
- GUILayout.Label(text, HotReloadWindowStyles.EmptyListText);
- }
- GUILayout.FlexibleSpace();
- } else {
- GUILayout.FlexibleSpace();
- if (HotReloadTimelineHelper.EventsTimeline.Count > 0 && GUILayout.Button("Clear")) {
- HotReloadTimelineHelper.ClearEntries();
- if (HotReloadWindow.Current) {
- HotReloadWindow.Current.Repaint();
- }
- }
- GUILayout.Space(3);
- }
- }
- if (!noteShown) {
- GUILayout.Space(2f);
- using (new EditorGUILayout.VerticalScope()) {
- RenderEntries(TimelineType.Timeline);
- }
- }
- }
- }
- }
- }
-
- internal static void RenderConsumption(LoginStatusResponse loginStatus) {
- if (loginStatus == null) {
- return;
- }
- EditorGUILayout.Space();
-
- EditorGUILayout.LabelField($"Hot Reload Limited", HotReloadWindowStyles.H3CenteredTitleStyle);
- EditorGUILayout.Space();
- if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.NetworkUnreachable) {
- EditorGUILayout.HelpBox("Something went wrong. Please check your internet connection.", MessageType.Warning);
- } else if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.UnrecoverableError) {
- EditorGUILayout.HelpBox("Something went wrong. Please contact support if the issue persists.", MessageType.Error);
- } else if (loginStatus.freeSessionFinished) {
- var now = DateTime.UtcNow;
- var sessionRefreshesAt = (now.AddDays(1).Date - now).Add(TimeSpan.FromMinutes(5));
- var sessionRefreshString = $"Next Session: {(sessionRefreshesAt.Hours > 0 ? $"{sessionRefreshesAt.Hours}h " : "")}{sessionRefreshesAt.Minutes}min";
- HotReloadGUIHelper.HelpBox(sessionRefreshString, MessageType.Warning, fontSize: 11);
- } else if (loginStatus.freeSessionRunning && loginStatus.freeSessionEndTime != null) {
- var sessionEndsAt = loginStatus.freeSessionEndTime.Value - DateTime.Now;
- var sessionString = $"Daily Session: {(sessionEndsAt.Hours > 0 ? $"{sessionEndsAt.Hours}h " : "")}{sessionEndsAt.Minutes}min Left";
- HotReloadGUIHelper.HelpBox(sessionString, MessageType.Info, fontSize: 11);
- } else if (loginStatus.freeSessionEndTime == null) {
- HotReloadGUIHelper.HelpBox("Daily Session: Make code changes to start", MessageType.Info, fontSize: 11);
- }
- }
- static bool _repaint;
- static bool _instantRepaint;
- static DateTime _lastRepaint;
- private EditorIndicationState.IndicationStatus _lastStatus;
- public override void Update() {
- if (EditorIndicationState.SpinnerActive) {
- _repaint = true;
- }
- if (EditorCodePatcher.DownloadRequired) {
- _repaint = true;
- }
- if (EditorIndicationState.IndicationIconPath == Spinner.SpinnerIconPath) {
- _repaint = true;
- }
- try {
- // workaround: hovering over non-buttons doesn't repain by default
- if (EditorWindow.mouseOverWindow == HotReloadWindow.Current) {
- _repaint = true;
- }
- if (EditorWindow.mouseOverWindow
- && EditorWindow.mouseOverWindow?.GetType() == typeof(PopupWindow)
- && HotReloadEventPopup.I.open
- ) {
- _repaint = true;
- }
- } catch (NullReferenceException) {
- // Unity randomly throws nullrefs when EditorWindow.mouseOverWindow gets accessed
- }
- if (_repaint && DateTime.UtcNow - _lastRepaint > TimeSpan.FromMilliseconds(33)) {
- _repaint = false;
- _instantRepaint = true;
- }
- // repaint on status change
- var status = EditorIndicationState.CurrentIndicationStatus;
- if (_lastStatus != status) {
- _lastStatus = status;
- _instantRepaint = true;
- }
- if (_instantRepaint) {
- Repaint();
- HotReloadEventPopup.I.Repaint();
- _instantRepaint = false;
- _repaint = false;
- _lastRepaint = DateTime.UtcNow;
- }
- }
-
- public static void RepaintInstant() {
- _instantRepaint = true;
- }
- private void RenderRecompileButton() {
- string recompileText = HotReloadWindowStyles.windowScreenWidth > Constants.RecompileButtonTextHideWidth ? " Recompile" : "";
- var recompileButton = new GUIContent(recompileText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Recompile));
- if (!GUILayout.Button(recompileButton, HotReloadWindowStyles.RecompileButton)) {
- return;
- }
- RecompileWithChecks();
- }
- public static void RecompileWithChecks() {
- var firstDialoguePass = HotReloadPrefs.RecompileDialogueShown
- || EditorUtility.DisplayDialog(
- title: "Hot Reload auto-applies changes",
- message: "Using the Recompile button is only necessary when Hot Reload fails to apply your changes. \n\nDo you wish to proceed?",
- ok: "Recompile",
- cancel: "Not now");
- HotReloadPrefs.RecompileDialogueShown = true;
- if (!firstDialoguePass) {
- return;
- }
- if (!ConfirmExitPlaymode("Using the Recompile button will stop Play Mode.\n\nDo you wish to proceed?")) {
- return;
- }
- Recompile();
- }
- #if UNITY_2020_1_OR_NEWER
- public static void SwitchToDebugMode() {
- CompilationPipeline.codeOptimization = CodeOptimization.Debug;
- HotReloadRunTab.Recompile();
- HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods);
- }
- #endif
- public static bool ConfirmExitPlaymode(string message) {
- return !Application.isPlaying
- || EditorUtility.DisplayDialog(
- title: "Stop Play Mode and Recompile?",
- message: message,
- ok: "Stop and Recompile",
- cancel: "Cancel");
- }
- public static bool recompiling;
- public static void Recompile() {
- recompiling = true;
- EditorApplication.isPlaying = false;
- CompileMethodDetourer.Reset();
- AssetDatabase.Refresh();
- // This forces the recompilation if no changes were made.
- // This is better UX because otherwise the recompile button is unresponsive
- // which can be extra annoying if there are compile error entries in the list
- if (!EditorApplication.isCompiling) {
- CompilationPipeline.RequestScriptCompilation();
- }
- }
-
- private void RenderIndicationButtons() {
- if (currentState.requestingDownloadAndRun || currentState.starting || currentState.stopping || currentState.redeemStage != RedeemStage.None) {
- return;
- }
-
- if (!currentState.running && (currentState.startupProgress?.Item1 ?? 0) == 0) {
- string startText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? " Start" : "";
- if (GUILayout.Button(new GUIContent(startText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Start)), HotReloadWindowStyles.StartButton)) {
- EditorCodePatcher.DownloadAndRun().Forget();
- }
- } else if (currentState.running && !currentState.starting) {
- if (HotReloadWindowStyles.windowScreenWidth > 150) {
- RenderRecompileButton();
- }
- string stopText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? " Stop" : "";
- if (GUILayout.Button(new GUIContent(stopText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Stop)), HotReloadWindowStyles.StopButton)) {
- if (!EditorCodePatcher.StoppedServerRecently()) {
- EditorCodePatcher.StopCodePatcher().Forget();
- }
- }
- }
- }
- void RenderIndicationPanel() {
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBox)) {
- RenderIndication();
- if (HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth) {
- GUILayout.FlexibleSpace();
- }
- RenderIndicationButtons();
- if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) {
- GUILayout.FlexibleSpace();
- }
- }
- if (currentState.requestingDownloadAndRun || currentState.starting) {
- RenderProgressBar();
- }
- if (HotReloadWindowStyles.windowScreenWidth > Constants.ConsumptionsHideWidth
- && HotReloadWindowStyles.windowScreenHeight > Constants.ConsumptionsHideHeight
- ) {
- RenderLicenseInfo(currentState);
- }
- }
- internal static void RenderLicenseInfo(HotReloadRunTabState currentState) {
- var showRedeem = currentState.redeemStage != RedeemStage.None;
- var showConsumptions = ShouldRenderConsumption(currentState);
- if (!showConsumptions && !showRedeem) {
- return;
- }
- using (new EditorGUILayout.VerticalScope()) {
- // space needed only for consumptions because of Stop/Start button's margin
- if (showConsumptions) {
- GUILayout.Space(6);
- }
- using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Section)) {
- if (showRedeem) {
- RedeemLicenseHelper.I.RenderStage(currentState);
- } else {
- RenderConsumption(currentState.loginStatus);
- GUILayout.Space(10);
- RenderLicenseInfo(currentState, currentState.loginStatus);
- RenderLicenseButtons(currentState);
- GUILayout.Space(10);
- }
- }
- GUILayout.Space(6);
- }
- }
-
- private Spinner _spinner = new Spinner(85);
- private void RenderIndication() {
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationBox)) {
- // icon box
- if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) {
- GUILayout.FlexibleSpace();
- }
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationHelpBox)) {
- var text = HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth ? $" {currentState.indicationStatusText}" : "";
- if (currentState.indicationIconPath == Spinner.SpinnerIconPath) {
- GUILayout.Label(new GUIContent(text, _spinner.GetIcon()), style: HotReloadWindowStyles.IndicationIcon);
- } else if (currentState.indicationIconPath != null) {
- var style = HotReloadWindowStyles.IndicationIcon;
- if (HotReloadTimelineHelper.alertIconString.ContainsValue(currentState.indicationIconPath)) {
- style = HotReloadWindowStyles.IndicationAlertIcon;
- }
- GUILayout.Label(new GUIContent(text, GUIHelper.GetLocalIcon(currentState.indicationIconPath)), style);
- }
- }
- }
- }
-
- static GUIStyle _openSettingsStyle;
- static GUIStyle openSettingsStyle => _openSettingsStyle ?? (_openSettingsStyle = new GUIStyle(GUI.skin.button) {
- fontStyle = FontStyle.Normal,
- fixedHeight = 25,
- });
-
- static GUILayoutOption[] _bigButtonHeight;
- public static GUILayoutOption[] bigButtonHeight => _bigButtonHeight ?? (_bigButtonHeight = new [] {GUILayout.Height(25)});
-
- private static GUIContent indieLicenseContent;
- private static GUIContent businessLicenseContent;
- internal static void RenderLicenseStatusInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool allowHide = true, bool verbose = false) {
- string message = null;
- MessageType messageType = default(MessageType);
- Action customGUI = null;
- GUIContent content = null;
- if (loginStatus == null) {
- // no info
- } else if (loginStatus.lastLicenseError != null) {
- messageType = !loginStatus.freeSessionFinished ? MessageType.Warning : MessageType.Error;
- message = GetMessageFromError(currentState, loginStatus.lastLicenseError);
- } else if (loginStatus.isTrial && !PackageConst.IsAssetStoreBuild) {
- message = $"Using Trial license, valid until {loginStatus.licenseExpiresAt.ToShortDateString()}";
- messageType = MessageType.Info;
- } else if (loginStatus.isIndieLicense) {
- if (verbose) {
- message = " Indie license active";
- messageType = MessageType.Info;
- customGUI = () => {
- if (loginStatus.licenseExpiresAt.Date != DateTime.MaxValue.Date) {
- EditorGUILayout.LabelField($"License will renew on {loginStatus.licenseExpiresAt.ToShortDateString()}.");
- EditorGUILayout.Space();
- }
- using (new GUILayout.HorizontalScope()) {
- HotReloadAboutTab.manageLicenseButton.OnGUI();
- HotReloadAboutTab.manageAccountButton.OnGUI();
- }
- EditorGUILayout.Space();
- };
- if (indieLicenseContent == null) {
- indieLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed"));
- }
- content = indieLicenseContent;
- }
- } else if (loginStatus.isBusinessLicense) {
- if (verbose) {
- message = " Business license active";
- messageType = MessageType.Info;
- if (businessLicenseContent == null) {
- businessLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed"));
- }
- content = businessLicenseContent;
- customGUI = () => {
- using (new GUILayout.HorizontalScope()) {
- HotReloadAboutTab.manageLicenseButton.OnGUI();
- HotReloadAboutTab.manageAccountButton.OnGUI();
- }
- EditorGUILayout.Space();
- };
- }
- }
- if (messageType != MessageType.Info && HotReloadPrefs.ErrorHidden && allowHide) {
- return;
- }
- if (message != null) {
- if (messageType != MessageType.Info) {
- using(new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.HelpBox(message, messageType);
- var style = HotReloadWindowStyles.HideButtonStyle;
- if (Event.current.type == EventType.Repaint) {
- style.fixedHeight = GUILayoutUtility.GetLastRect().height;
- }
- if (allowHide) {
- if (GUILayout.Button("Hide", style)) {
- HotReloadPrefs.ErrorHidden = true;
- }
- }
- }
- } else if (content != null) {
- EditorGUILayout.LabelField(content);
- EditorGUILayout.Space();
- } else {
- EditorGUILayout.LabelField(message);
- EditorGUILayout.Space();
- }
- customGUI?.Invoke();
- }
- }
- const string assetStoreProInfo = "Unity Pro/Enterprise users from company with your number of employees require a Business license. Please upgrade your license on our website.";
- internal static void RenderBusinessLicenseInfo(GUIStyle style) {
- GUILayout.Space(8);
- using (new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.HelpBox(assetStoreProInfo, MessageType.Info);
- if (Event.current.type == EventType.Repaint) {
- style.fixedHeight = GUILayoutUtility.GetLastRect().height;
- }
- if (GUILayout.Button("Upgrade", style)) {
- Application.OpenURL(Constants.ProductPurchaseBusinessURL);
- }
- }
- }
-
- internal static void RenderIndieLicenseInfo(GUIStyle style) {
- string message = "Unity Plus users require an Indie license. Please upgrade your license on our website.";
- GUILayout.Space(8);
- using (new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.HelpBox(message, MessageType.Info);
- if (Event.current.type == EventType.Repaint) {
- style.fixedHeight = GUILayoutUtility.GetLastRect().height;
- }
- if (GUILayout.Button("Upgrade", style)) {
- Application.OpenURL(Constants.ProductPurchaseURL);
- }
- }
- }
- const string GetLicense = "Get License";
- const string ContactSupport = "Contact Support";
- const string UpgradeLicense = "Upgrade License";
- const string ManageLicense = "Manage License";
- internal static Dictionary<string, LicenseErrorData> _licenseErrorData;
- internal static Dictionary<string, LicenseErrorData> LicenseErrorData => _licenseErrorData ?? (_licenseErrorData = new Dictionary<string, LicenseErrorData> {
- { "DeviceNotLicensedException", new LicenseErrorData(description: "Another device is using your license. Please reach out to customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport) },
- { "DeviceBlacklistedException", new LicenseErrorData(description: "You device has been blacklisted.") },
- { "DateHeaderInvalidException", new LicenseErrorData(description: $"Your license is not working because your computer's clock is incorrect. Please set the clock to the correct time to restore your license.") },
- { "DateTimeCheatingException", new LicenseErrorData(description: $"Your license is not working because your computer's clock is incorrect. Please set the clock to the correct time to restore your license.") },
- { "LicenseActivationException", new LicenseErrorData(description: "An error has occured while activating your license. Please contact customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport) },
- { "LicenseDeletedException", new LicenseErrorData(description: $"Your license has been deleted. Please contact customer support for assistance.", showBuyButton: true, buyButtonText: GetLicense, showSupportButton: true, supportButtonText: ContactSupport) },
- { "LicenseDisabledException", new LicenseErrorData(description: $"Your license has been disabled. Please contact customer support for assistance.", showBuyButton: true, buyButtonText: GetLicense, showSupportButton: true, supportButtonText: ContactSupport) },
- { "LicenseExpiredException", new LicenseErrorData(description: $"Your license has expired. Please renew your license subscription using the 'Upgrade License' button below and login with your email/password to activate your license.", showBuyButton: true, buyButtonText: UpgradeLicense, showManageLicenseButton: true, manageLicenseButtonText: ManageLicense) },
- { "LicenseInactiveException", new LicenseErrorData(description: $"Your license is currenty inactive. Please login with your email/password to activate your license.") },
- { "LocalLicenseException", new LicenseErrorData(description: $"Your license file was damaged or corrupted. Please login with your email/password to refresh your license file.") },
- // Note: obsolete
- { "MissingParametersException", new LicenseErrorData(description: "An account already exists for this device. Please login with your existing email/password.", showBuyButton: true, buyButtonText: GetLicense) },
- { "NetworkException", new LicenseErrorData(description: "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists.", showSupportButton: true, supportButtonText: ContactSupport) },
- { "TrialLicenseExpiredException", new LicenseErrorData(description: $"Your trial has expired. Activate a license with unlimited usage or continue using the Free version. View available plans on our website.", showBuyButton: true, buyButtonText: UpgradeLicense) },
- { "InvalidCredentialException", new LicenseErrorData(description: "Incorrect email/password. You can find your initial password in the sign-up email.") },
- // Note: activating free trial with email is not supported anymore. This error shouldn't happen which is why we should rather user the fallback
- // { "LicenseNotFoundException", new LicenseErrorData(description: "The account you're trying to access doesn't seem to exist yet. Please enter your email address to create a new account and receive a trial license.", showLoginButton: true, loginButtonText: CreateAccount) },
- { "LicenseIncompatibleException", new LicenseErrorData(description: "Please upgrade your license to continue using hotreload with Unity Pro.", showManageLicenseButton: true, manageLicenseButtonText: ManageLicense) },
- });
- internal static LicenseErrorData defaultLicenseErrorData = new LicenseErrorData(description: "We apologize, an error happened while verifying your license. Please reach out to customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport);
- internal static string GetMessageFromError(HotReloadRunTabState currentState, string error) {
- if (PackageConst.IsAssetStoreBuild && error == "TrialLicenseExpiredException") {
- return assetStoreProInfo;
- }
- return GetLicenseErrorDataOrDefault(currentState, error).description;
- }
-
- internal static LicenseErrorData GetLicenseErrorDataOrDefault(HotReloadRunTabState currentState, string error) {
- if (currentState.loginStatus?.isFree == true) {
- return default(LicenseErrorData);
- }
- if (currentState.loginStatus == null || string.IsNullOrEmpty(error) && (!currentState.loginStatus.isLicensed || currentState.loginStatus.isTrial)) {
- return new LicenseErrorData(null, showBuyButton: true, buyButtonText: GetLicense);
- }
- if (string.IsNullOrEmpty(error)) {
- return default(LicenseErrorData);
- }
- if (!LicenseErrorData.ContainsKey(error)) {
- return defaultLicenseErrorData;
- }
- return LicenseErrorData[error];
- }
- internal static void RenderBuyLicenseButton(string buyLicenseButton) {
- OpenURLButton.Render(buyLicenseButton, Constants.ProductPurchaseURL);
- }
- static void RenderLicenseActionButtons(HotReloadRunTabState currentState) {
- var errInfo = GetLicenseErrorDataOrDefault(currentState, currentState.loginStatus?.lastLicenseError);
- if (errInfo.showBuyButton || errInfo.showManageLicenseButton) {
- using(new EditorGUILayout.HorizontalScope()) {
- if (errInfo.showBuyButton) {
- RenderBuyLicenseButton(errInfo.buyButtonText);
- }
- if (errInfo.showManageLicenseButton && !HotReloadPrefs.ErrorHidden) {
- OpenURLButton.Render(errInfo.manageLicenseButtonText, Constants.ManageLicenseURL);
- }
- }
- }
- if (errInfo.showLoginButton && GUILayout.Button(errInfo.loginButtonText, openSettingsStyle)) {
- // show license section
- HotReloadWindow.Current.SelectTab(typeof(HotReloadSettingsTab));
- HotReloadWindow.Current.SettingsTab.FocusLicenseFoldout();
- }
- if (errInfo.showSupportButton && !HotReloadPrefs.ErrorHidden) {
- OpenURLButton.Render(errInfo.supportButtonText, Constants.ContactURL);
- }
- if (currentState.loginStatus?.lastLicenseError != null) {
- HotReloadAboutTab.reportIssueButton.OnGUI();
- }
- }
-
- internal static void RenderLicenseInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool verbose = false, bool allowHide = true, string overrideActionButton = null, bool showConsumptions = false) {
- HotReloadPrefs.ShowLogin = EditorGUILayout.Foldout(HotReloadPrefs.ShowLogin, "Hot Reload License", true, HotReloadWindowStyles.FoldoutStyle);
- if (HotReloadPrefs.ShowLogin) {
- EditorGUILayout.Space();
- if ((loginStatus?.isLicensed != true && showConsumptions) && !(loginStatus == null || loginStatus.isFree)) {
- RenderConsumption(loginStatus);
- }
- RenderLicenseStatusInfo(currentState, loginStatus: loginStatus, allowHide: allowHide, verbose: verbose);
- RenderLicenseInnerPanel(currentState, overrideActionButton: overrideActionButton);
-
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- }
- }
- internal void RenderPromoCodes() {
- HotReloadPrefs.ShowPromoCodes = EditorGUILayout.Foldout(HotReloadPrefs.ShowPromoCodes, "Promo Codes", true, HotReloadWindowStyles.FoldoutStyle);
- if (!HotReloadPrefs.ShowPromoCodes) {
- return;
- }
- if (promoCodeActivatedThisSession) {
- EditorGUILayout.HelpBox($"Your promo code has been successfully activated. Free trial has been extended by 3 months.", MessageType.Info);
- } else {
- if (promoCodeError != null && promoCodeErrorType != MessageType.None) {
- EditorGUILayout.HelpBox(promoCodeError, promoCodeErrorType);
- }
- EditorGUILayout.LabelField("Promo code");
- _pendingPromoCode = EditorGUILayout.TextField(_pendingPromoCode);
- EditorGUILayout.Space();
- using (new EditorGUI.DisabledScope(_requestingActivatePromoCode)) {
- if (GUILayout.Button("Activate promo code", HotReloadRunTab.bigButtonHeight)) {
- RequestActivatePromoCode().Forget();
- }
- }
- }
-
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- }
-
- private async Task RequestActivatePromoCode() {
- _requestingActivatePromoCode = true;
- try {
- var resp = await RequestHelper.RequestActivatePromoCode(_pendingPromoCode);
- if (resp != null && resp.error == null) {
- promoCodeActivatedThisSession = true;
- } else {
- var requestError = resp?.error ?? "Network error";
- var errorType = ToErrorType(requestError);
- promoCodeError = ToPrettyErrorMessage(errorType);
- promoCodeErrorType = ToMessageType(errorType);
- }
- } finally {
- _requestingActivatePromoCode = false;
- }
- }
- PromoCodeErrorType ToErrorType(string error) {
- switch (error) {
- case "Input is missing": return PromoCodeErrorType.MISSING_INPUT;
- case "only POST is supported": return PromoCodeErrorType.INVALID_HTTP_METHOD;
- case "body is not a valid json": return PromoCodeErrorType.BODY_INVALID;
- case "Promo code is not found": return PromoCodeErrorType.PROMO_CODE_NOT_FOUND;
- case "Promo code already claimed": return PromoCodeErrorType.PROMO_CODE_CLAIMED;
- case "Promo code expired": return PromoCodeErrorType.PROMO_CODE_EXPIRED;
- case "License not found": return PromoCodeErrorType.LICENSE_NOT_FOUND;
- case "License is not a trial": return PromoCodeErrorType.LICENSE_NOT_TRIAL;
- case "License already extended": return PromoCodeErrorType.LICENSE_ALREADY_EXTENDED;
- case "conditionalCheckFailed": return PromoCodeErrorType.CONDITIONAL_CHECK_FAILED;
- }
- if (error.Contains("Updating License Failed with error")) {
- return PromoCodeErrorType.UPDATING_LICENSE_FAILED;
- } else if (error.Contains("Unknown exception")) {
- return PromoCodeErrorType.UNKNOWN_EXCEPTION;
- } else if (error.Contains("Unsupported path")) {
- return PromoCodeErrorType.UNSUPPORTED_PATH;
- }
- return PromoCodeErrorType.NONE;
- }
- string ToPrettyErrorMessage(PromoCodeErrorType errorType) {
- var defaultMsg = "We apologize, an error happened while activating your promo code. Please reach out to customer support for assistance.";
- switch (errorType) {
- case PromoCodeErrorType.MISSING_INPUT:
- case PromoCodeErrorType.INVALID_HTTP_METHOD:
- case PromoCodeErrorType.BODY_INVALID:
- case PromoCodeErrorType.UNKNOWN_EXCEPTION:
- case PromoCodeErrorType.UNSUPPORTED_PATH:
- case PromoCodeErrorType.LICENSE_NOT_FOUND:
- case PromoCodeErrorType.UPDATING_LICENSE_FAILED:
- case PromoCodeErrorType.LICENSE_NOT_TRIAL:
- return defaultMsg;
- case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return "Your promo code is invalid. Please ensure that you have entered the correct promo code.";
- case PromoCodeErrorType.PROMO_CODE_CLAIMED: return "Your promo code has already been used.";
- case PromoCodeErrorType.PROMO_CODE_EXPIRED: return "Your promo code has expired.";
- case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return "Your license has already been activated with a promo code. Only one promo code activation per license is allowed.";
- case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return "We encountered an error while activating your promo code. Please try again. If the issue persists, please contact our customer support team for assistance.";
- case PromoCodeErrorType.NONE: return "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists.";
- default: return defaultMsg;
- }
- }
- MessageType ToMessageType(PromoCodeErrorType errorType) {
- switch (errorType) {
- case PromoCodeErrorType.MISSING_INPUT: return MessageType.Error;
- case PromoCodeErrorType.INVALID_HTTP_METHOD: return MessageType.Error;
- case PromoCodeErrorType.BODY_INVALID: return MessageType.Error;
- case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return MessageType.Warning;
- case PromoCodeErrorType.PROMO_CODE_CLAIMED: return MessageType.Warning;
- case PromoCodeErrorType.PROMO_CODE_EXPIRED: return MessageType.Warning;
- case PromoCodeErrorType.LICENSE_NOT_FOUND: return MessageType.Error;
- case PromoCodeErrorType.LICENSE_NOT_TRIAL: return MessageType.Error;
- case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return MessageType.Warning;
- case PromoCodeErrorType.UPDATING_LICENSE_FAILED: return MessageType.Error;
- case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return MessageType.Error;
- case PromoCodeErrorType.UNKNOWN_EXCEPTION: return MessageType.Error;
- case PromoCodeErrorType.UNSUPPORTED_PATH: return MessageType.Error;
- case PromoCodeErrorType.NONE: return MessageType.Error;
- default: return MessageType.Error;
- }
- }
- public static void RenderLicenseButtons(HotReloadRunTabState currentState) {
- RenderLicenseActionButtons(currentState);
- }
- internal static void RenderLicenseInnerPanel(HotReloadRunTabState currentState, string overrideActionButton = null, bool renderLogout = true) {
- EditorGUILayout.LabelField("Email");
- GUI.SetNextControlName("email");
- _pendingEmail = EditorGUILayout.TextField(string.IsNullOrEmpty(_pendingEmail) ? HotReloadPrefs.LicenseEmail : _pendingEmail);
- _pendingEmail = _pendingEmail.Trim();
- EditorGUILayout.LabelField("Password");
- GUI.SetNextControlName("password");
- _pendingPassword = EditorGUILayout.PasswordField(string.IsNullOrEmpty(_pendingPassword) ? HotReloadPrefs.LicensePassword : _pendingPassword);
-
- RenderSwitchAuthMode();
-
- var e = Event.current;
- using(new EditorGUI.DisabledScope(currentState.requestingLoginInfo)) {
- var btnLabel = overrideActionButton;
- if (String.IsNullOrEmpty(overrideActionButton)) {
- btnLabel = "Login";
- }
- using (new EditorGUILayout.HorizontalScope()) {
- var focusedControl = GUI.GetNameOfFocusedControl();
- if (GUILayout.Button(btnLabel, bigButtonHeight)
- || (focusedControl == "email"
- || focusedControl == "password")
- && e.type == EventType.KeyUp
- && (e.keyCode == KeyCode.Return
- || e.keyCode == KeyCode.KeypadEnter)
- ) {
- var error = ValidateEmail(_pendingEmail);
- if (!string.IsNullOrEmpty(error)) {
- _activateInfoMessage = new Tuple<string, MessageType>(error, MessageType.Warning);
- } else if (string.IsNullOrEmpty(_pendingPassword)) {
- _activateInfoMessage = new Tuple<string, MessageType>("Please enter your password.", MessageType.Warning);
- } else {
- HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab));
- _activateInfoMessage = null;
- if (RedeemLicenseHelper.I.RedeemStage == RedeemStage.Login) {
- RedeemLicenseHelper.I.FinishRegistration(RegistrationOutcome.Indie);
- }
- if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) {
- LoginOnDownloadAndRun(new LoginData(email: _pendingEmail, password: _pendingPassword)).Forget();
- } else {
- EditorCodePatcher.RequestLogin(_pendingEmail, _pendingPassword).Forget();
- }
- }
- }
- if (renderLogout) {
- RenderLogout(currentState);
- }
- }
- }
- if (_activateInfoMessage != null && (e.type == EventType.Layout || e.type == EventType.Repaint)) {
- EditorGUILayout.HelpBox(_activateInfoMessage.Item1, _activateInfoMessage.Item2);
- }
- }
- public static string ValidateEmail(string email) {
- if (string.IsNullOrEmpty(email)) {
- return "Please enter your email address.";
- } else if (!EditorWindowHelper.IsValidEmailAddress(email)) {
- return "Please enter a valid email address.";
- } else if (email.Contains("+")) {
- return "Mail extensions (in a form of 'username+suffix@example.com') are not supported yet. Please provide your original email address (such as 'username@example.com' without '+suffix' part) as we're working on resolving this issue.";
- }
- return null;
- }
- public static void RenderLogout(HotReloadRunTabState currentState) {
- if (currentState.loginStatus?.isLicensed != true) {
- return;
- }
- if (GUILayout.Button("Logout", bigButtonHeight)) {
- HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab));
- if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) {
- LogoutOnDownloadAndRun().Forget();
- } else {
- RequestLogout().Forget();
- }
- }
- }
-
- async static Task LoginOnDownloadAndRun(LoginData loginData = null) {
- var ok = await EditorCodePatcher.DownloadAndRun(loginData);
- if (ok && loginData != null) {
- HotReloadPrefs.ErrorHidden = false;
- HotReloadPrefs.LicenseEmail = loginData.email;
- HotReloadPrefs.LicensePassword = loginData.password;
- }
- }
- async static Task LogoutOnDownloadAndRun() {
- var ok = await EditorCodePatcher.DownloadAndRun();
- if (!ok) {
- return;
- }
- await RequestLogout();
- }
- private async static Task RequestLogout() {
- int i = 0;
- while (!EditorCodePatcher.Running && i < 100) {
- await Task.Delay(100);
- i++;
- }
- var resp = await RequestHelper.RequestLogout();
- if (!EditorCodePatcher.RequestingLoginInfo && resp != null) {
- EditorCodePatcher.HandleStatus(resp);
- }
- }
- private static void RenderSwitchAuthMode() {
- var color = EditorGUIUtility.isProSkin ? new Color32(0x3F, 0x9F, 0xFF, 0xFF) : new Color32(0x0F, 0x52, 0xD7, 0xFF);
- if (HotReloadGUIHelper.LinkLabel("Forgot password?", 12, FontStyle.Normal, TextAnchor.MiddleLeft, color)) {
- if (EditorUtility.DisplayDialog("Recover password", "Use company code 'naughtycult' and the email you signed up with in order to recover your account.", "Open in browser", "Cancel")) {
- Application.OpenURL(Constants.ForgotPasswordURL);
- }
- }
- }
-
- Texture2D _greenTextureLight;
- Texture2D _greenTextureDark;
- Texture2D GreenTexture => EditorGUIUtility.isProSkin
- ? _greenTextureDark ? _greenTextureDark : (_greenTextureDark = MakeTexture(0.5f))
- : _greenTextureLight ? _greenTextureLight : (_greenTextureLight = MakeTexture(0.85f));
-
- private void RenderProgressBar() {
- if (currentState.downloadRequired && !currentState.downloadStarted) {
- return;
- }
-
- using(var scope = new EditorGUILayout.VerticalScope(HotReloadWindowStyles.MiddleCenterStyle)) {
- float progress;
- var bg = HotReloadWindowStyles.ProgressBarBarStyle.normal.background;
- try {
- HotReloadWindowStyles.ProgressBarBarStyle.normal.background = GreenTexture;
- var barRect = scope.rect;
- barRect.height = 25;
- if (currentState.downloadRequired) {
- barRect.width = barRect.width - 65;
- using (new EditorGUILayout.HorizontalScope()) {
- progress = EditorCodePatcher.DownloadProgress;
- EditorGUI.ProgressBar(barRect, Mathf.Clamp(progress, 0f, 1f), "");
- if (GUI.Button(new Rect(barRect) { x = barRect.x + barRect.width + 5, height = barRect.height, width = 60 }, new GUIContent(" Info", GUIHelper.GetLocalIcon("alert_info")))) {
- Application.OpenURL(Constants.AdditionalContentURL);
- }
- }
- } else {
- progress = EditorCodePatcher.Stopping ? 1 : Mathf.Clamp(EditorCodePatcher.StartupProgress?.Item1 ?? 0f, 0f, 1f);
- EditorGUI.ProgressBar(barRect, progress, "");
- }
- GUILayout.Space(barRect.height);
- } finally {
- HotReloadWindowStyles.ProgressBarBarStyle.normal.background = bg;
- }
- }
- }
- private Texture2D MakeTexture(float maxHue) {
- var width = 11;
- var height = 11;
- Color[] pix = new Color[width * height];
- for (int y = 0; y < height; y++) {
- var middle = Math.Ceiling(height / (double)2);
- var maxGreen = maxHue;
- var yCoord = y + 1;
- var green = maxGreen - Math.Abs(yCoord - middle) * 0.02;
- for (int x = 0; x < width; x++) {
- pix[y * width + x] = new Color(0.1f, (float)green, 0.1f, 1.0f);
- }
- }
- var result = new Texture2D(width, height);
- result.SetPixels(pix);
- result.Apply();
- return result;
- }
-
-
- /*
- [MenuItem("codepatcher/restart")]
- public static void TestRestart() {
- CodePatcherCLI.Restart(Application.dataPath, false);
- }
- */
-
- }
- internal static class HotReloadGUIHelper {
- public static bool LinkLabel(string labelText, int fontSize, FontStyle fontStyle, TextAnchor alignment, Color? color = null) {
- var stl = EditorStyles.label;
- // copy
- var origSize = stl.fontSize;
- var origStyle = stl.fontStyle;
- var origAnchor = stl.alignment;
- var origColor = stl.normal.textColor;
- // temporarily modify the built-in style
- stl.fontSize = fontSize;
- stl.fontStyle = fontStyle;
- stl.alignment = alignment;
- stl.normal.textColor = color ?? origColor;
- stl.active.textColor = color ?? origColor;
- stl.focused.textColor = color ?? origColor;
- stl.hover.textColor = color ?? origColor;
- try {
- return GUILayout.Button(labelText, stl);
- } finally{
- // set the editor style (stl) back to normal
- stl.fontSize = origSize;
- stl.fontStyle = origStyle;
- stl.alignment = origAnchor;
- stl.normal.textColor = origColor;
- stl.active.textColor = origColor;
- stl.focused.textColor = origColor;
- stl.hover.textColor = origColor;
- }
- }
- public static void HelpBox(string message, MessageType type, int fontSize) {
- var _fontSize = EditorStyles.helpBox.fontSize;
- try {
- EditorStyles.helpBox.fontSize = fontSize;
- EditorGUILayout.HelpBox(message, type);
- } finally {
- EditorStyles.helpBox.fontSize = _fontSize;
- }
- }
- }
- internal enum PromoCodeErrorType {
- NONE,
- MISSING_INPUT,
- INVALID_HTTP_METHOD,
- BODY_INVALID,
- PROMO_CODE_NOT_FOUND,
- PROMO_CODE_CLAIMED,
- PROMO_CODE_EXPIRED,
- LICENSE_NOT_FOUND,
- LICENSE_NOT_TRIAL,
- LICENSE_ALREADY_EXTENDED,
- UPDATING_LICENSE_FAILED,
- CONDITIONAL_CHECK_FAILED,
- UNKNOWN_EXCEPTION,
- UNSUPPORTED_PATH,
- }
- internal class LoginData {
- public readonly string email;
- public readonly string password;
- public LoginData(string email, string password) {
- this.email = email;
- this.password = password;
- }
- }
- }
|