Commit 718d71c8 authored by UnityMethodPatcher CI's avatar UnityMethodPatcher CI
Browse files

[CI] Changes for 1-12-10

Showing with 420 additions and 75 deletions
+420 -75
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using SingularityGroup.HotReload.Newtonsoft.Json;
using UnityEngine;
#if !UNITY_EDITOR_OSX
using System;
#endif
namespace SingularityGroup.HotReload.Editor.Cli {
internal static class CliUtils {
static readonly string projectIdentifier = GetProjectIdentifier();
class Config {
public bool singleInstance;
}
public static string GetProjectIdentifier() {
if (File.Exists(PackageConst.ConfigFileName)) {
var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFileName));
if (config.singleInstance) {
return null;
}
}
var path = Path.GetDirectoryName(UnityHelper.DataPath);
var name = new DirectoryInfo(path).Name;
using (SHA256 sha256 = SHA256.Create()) {
byte[] inputBytes = Encoding.UTF8.GetBytes(path);
byte[] hashBytes = sha256.ComputeHash(inputBytes);
var hash = BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 6).ToUpper();
return $"{name}-{hash}";
}
}
public static string GetTempDownloadFilePath(string osxFileName) {
if (UnityHelper.Platform == RuntimePlatform.OSXEditor) {
// project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app)
......@@ -21,7 +45,11 @@ namespace SingularityGroup.HotReload.Editor.Cli {
// project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app)
return Path.GetFullPath(PackageConst.LibraryCachePath + "/HotReloadServerTemp");
} else {
return Path.Combine(Path.GetTempPath(), "HotReloadTemp");
if (projectIdentifier != null) {
return Path.Combine(Path.GetTempPath(), "HotReloadTemp", projectIdentifier);
} else {
return Path.Combine(Path.GetTempPath(), "HotReloadTemp");
}
}
}
......
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
using SingularityGroup.HotReload.Newtonsoft.Json;
using UnityEditor;
......@@ -39,10 +41,10 @@ namespace SingularityGroup.HotReload.Editor.Cli {
}
internal static async Task StartAsync(bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData = null) {
await Prepare().ConfigureAwait(false);
var port = await Prepare().ConfigureAwait(false);
await ThreadUtility.SwitchToThreadPool();
StartArgs args;
if (TryGetStartArgs(UnityHelper.DataPath, exposeServerToNetwork, allAssetChanges, createNoWindow, loginData, out args)) {
if (TryGetStartArgs(UnityHelper.DataPath, exposeServerToNetwork, allAssetChanges, createNoWindow, loginData, port, out args)) {
await controller.Start(args);
}
}
......@@ -63,7 +65,7 @@ namespace SingularityGroup.HotReload.Editor.Cli {
#pragma warning restore CS0649
}
static bool TryGetStartArgs(string dataPath, bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData, out StartArgs args) {
static bool TryGetStartArgs(string dataPath, bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData, int port, out StartArgs args) {
string serverDir;
if(!CliUtils.TryFindServerDir(out serverDir)) {
Log.Warning($"Failed to start the Hot Reload Server. " +
......@@ -112,7 +114,7 @@ namespace SingularityGroup.HotReload.Editor.Cli {
}
var searchAssemblies = string.Join(";", CodePatcher.I.GetAssemblySearchPaths());
var cliArguments = $@"-u ""{unityProjDir}"" -s ""{slnPath}"" -t ""{cliTempDir}"" -a ""{searchAssemblies}"" -ver ""{PackageConst.Version}"" -proc ""{Process.GetCurrentProcess().Id}"" -assets ""{allAssetChanges}""";
var cliArguments = $@"-u ""{unityProjDir}"" -s ""{slnPath}"" -t ""{cliTempDir}"" -a ""{searchAssemblies}"" -ver ""{PackageConst.Version}"" -proc ""{Process.GetCurrentProcess().Id}"" -assets ""{allAssetChanges}"" -p ""{port}""";
if (loginData != null) {
cliArguments += $@" -email ""{loginData.email}"" -pass ""{loginData.password}""";
}
......@@ -132,15 +134,65 @@ namespace SingularityGroup.HotReload.Editor.Cli {
return true;
}
static async Task Prepare() {
private static int DiscoverFreePort() {
var maxAttempts = 10;
for (int attempt = 0; attempt < maxAttempts; attempt++) {
var port = RequestHelper.defaultPort + attempt;
if (IsPortInUse(port)) {
continue;
}
return port;
}
// we give up at this point
return RequestHelper.defaultPort + maxAttempts;
}
public static bool IsPortInUse(int port) {
// Note that there is a racecondition that a port gets occupied after checking.
// However, it will very rare someone will run into this.
#if UNITY_EDITOR_WIN
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] activeTcpListeners = ipGlobalProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in activeTcpListeners) {
if (endPoint.Port == port) {
return true;
}
}
return false;
#else
try {
using (TcpClient tcpClient = new TcpClient()) {
tcpClient.Connect(IPAddress.Loopback, port); // Try to connect to the specified port
return true;
}
} catch (SocketException) {
return false;
} catch (Exception e) {
Log.Exception(e);
// act as if the port is allocated
return true;
}
#endif
}
static async Task<int> Prepare() {
await ThreadUtility.SwitchToMainThread();
var dataPath = UnityHelper.DataPath;
await ProjectGeneration.ProjectGeneration.EnsureSlnAndCsprojFiles(dataPath);
await PrepareBuildInfoAsync();
PrepareSystemPathsFile();
var port = DiscoverFreePort();
HotReloadState.ServerPort = port;
RequestHelper.SetServerPort(port);
return port;
}
static bool didLogWarning;
internal static async Task PrepareBuildInfoAsync() {
await ThreadUtility.SwitchToMainThread();
var buildInfoInput = await BuildInfoHelper.GetGenerateBuildInfoInput();
......@@ -148,8 +200,13 @@ namespace SingularityGroup.HotReload.Editor.Cli {
try {
var buildInfo = BuildInfoHelper.GenerateBuildInfoThreaded(buildInfoInput);
PrepareBuildInfo(buildInfo);
} catch {
// ignore, we will warn when making a build
} catch (Exception e) {
if (!didLogWarning) {
Log.Warning($"Preparing build info failed! On-device functionality might not work. Exception: {e}");
didLogWarning = true;
} else {
Log.Debug($"Preparing build info failed! On-device functionality might not work. Exception: {e}");
}
}
});
}
......
......@@ -95,7 +95,6 @@ namespace SingularityGroup.HotReload.Editor.Cli {
var executableScriptPath = Path.Combine(Path.GetTempPath(), "Start_HotReloadServer.command");
// You don't need to copy the cli executable on mac
// omit hashbang line, let shell use the default interpreter (easier than detecting your default shell beforehand)
File.WriteAllText(executableScriptPath, $"echo $$ > \"{pidFilePath}\"" +
$"\ncd \"{Environment.CurrentDirectory}\"" + // set cwd because 'open' launches script with $HOME as cwd.
$"\n\"{executablePath}\" {args.cliArguments} || read");
......
......@@ -17,6 +17,7 @@ namespace SingularityGroup.HotReload.Editor {
public const string TroubleshootingURL = "https://hotreload.net/documentation/troubleshooting";
public const string RecompileTroubleshootingURL = TroubleshootingURL + "#unity-recompiles-every-time-i-enterexit-playmode";
public const string FeaturesDocumentationURL = DocumentationURL + "/features";
public const string MultipleEditorsURL = DocumentationURL + "/multiple-editors";
public const string VoteForAwardURL = "https://awards.unity.com/#best-development-tool";
public const string UnityStoreRateAppURL = "https://assetstore.unity.com/packages/slug/254358#reviews";
public const string ChangelogURL = WebsiteURL + "/changelog";
......
......@@ -28,6 +28,7 @@ namespace SingularityGroup.HotReload.Editor {
public string[] assetBlacklist;
public bool changePlaymodeTint;
public bool disableCompilingFromEditorScripts;
public bool enableInspectorFreezeFix;
}
[InitializeOnLoad]
......@@ -50,7 +51,7 @@ namespace SingularityGroup.HotReload.Editor {
internal static PatchStatus patchStatus = PatchStatus.None;
internal static event Action OnPatchHandled;
internal static Config config;
......@@ -167,7 +168,7 @@ namespace SingularityGroup.HotReload.Editor {
}
public static bool TryRecompileUnsupportedChanges() {
if (!HotReloadPrefs.AutoRecompileUnsupportedChanges
if (!HotReloadPrefs.AutoRecompileUnsupportedChanges
|| HotReloadTimelineHelper.UnsupportedChangesCount == 0
&& (!HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges || HotReloadTimelineHelper.PartiallySupportedChangesCount == 0)
|| _compileError
......@@ -280,9 +281,7 @@ namespace SingularityGroup.HotReload.Editor {
}
private static void UpdateHost() {
string host = "127.0.0.1";
var rootPath = Path.GetFullPath(".");
RequestHelper.SetServerInfo(new PatchServerInfo(host, null, rootPath));
RequestHelper.SetServerInfo(new PatchServerInfo(RequestHelper.defaultServerHost, HotReloadState.ServerPort, null, Path.GetFullPath(".")));
}
static void OnIntervalThreaded(object o) {
......@@ -322,8 +321,8 @@ namespace SingularityGroup.HotReload.Editor {
if (responseWarning.Contains("Scripts have compile errors")) {
Log.Error(responseWarning);
} else {
Log.Warning(responseWarning);
}
Log.Warning(responseWarning);
}
if (responseWarning.Contains("Multidimensional arrays are not supported")) {
await ThreadUtility.SwitchToMainThread();
......@@ -337,9 +336,28 @@ namespace SingularityGroup.HotReload.Editor {
internal static bool firstPatchAttempted;
static void OnIntervalMainThread() {
RequestServerInfo();
HotReloadSuggestionsHelper.Check();
// Moved from RequestServerInfo to avoid GC allocations when HR is not active
// Repaint if the running Status has changed since the layout changes quite a bit
if (running != ServerHealthCheck.I.IsServerHealthy) {
if (HotReloadWindow.Current) {
HotReloadRunTab.RepaintInstant();
}
running = ServerHealthCheck.I.IsServerHealthy;
}
if (!running) {
startupCompletedAt = null;
}
if (!running && !StartedServerRecently()) {
// Reset startup progress
startupProgress = null;
}
if(ServerHealthCheck.I.IsServerHealthy) {
// NOTE: avoid calling this method when HR is not running to avoid allocations
RequestServerInfo();
TryPrepareBuildInfo();
if (!requestingCompile && (!config.patchEditModeOnlyOnEditorFocus || Application.isPlaying || UnityEditorInternal.InternalEditorUtility.isApplicationActive)) {
RequestHelper.PollMethodPatches(HotReloadState.LastPatchId, resp => HandleResponseReceived(resp));
......@@ -723,6 +741,7 @@ namespace SingularityGroup.HotReload.Editor {
return;
}
CodePatcher.I.ClearPatchedMethods();
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
try {
requestingStop = true;
await HotReloadCli.StopAsync().ConfigureAwait(false);
......@@ -879,27 +898,13 @@ namespace SingularityGroup.HotReload.Editor {
var waitMs = (int)Mathf.Clamp(pollFrequency - ((DateTime.Now.Ticks / (float)TimeSpan.TicksPerMillisecond) - lastServerPoll), 0, pollFrequency);
await Task.Delay(waitMs);
var oldRunning = running;
var newRunning = ServerHealthCheck.I.IsServerHealthy;
running = newRunning;
if (running) {
var resp = await RequestHelper.GetLoginStatus(30);
HandleStatus(resp);
} else {
startupCompletedAt = null;
}
if (!running && !StartedServerRecently()) {
// Reset startup progress
startupProgress = null;
if (!ServerHealthCheck.I.IsServerHealthy) {
return;
}
// Repaint if the running Status has changed since the layout changes quite a bit
if (oldRunning != newRunning && HotReloadWindow.Current) {
HotReloadRunTab.RepaintInstant();
}
var resp = await RequestHelper.GetLoginStatus(30);
HandleStatus(resp);
lastServerPoll = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
}
......
......@@ -76,8 +76,21 @@ namespace SingularityGroup.HotReload.Editor {
private static bool SpinnerCompletedMinDuration => DateTime.UtcNow - spinnerStartedAt > TimeSpan.FromMilliseconds(MinSpinnerDuration);
private static IndicationStatus GetIndicationStatus() {
var status = GetIndicationStatusCore();
var newStatusIsSpinner = SpinnerIndications.Contains(status);
var latestStatusIsSpinner = SpinnerIndications.Contains(latestStatus);
// Note: performance sensitive code, don't use Link
bool newStatusIsSpinner = false;
for (var i = 0; i < SpinnerIndications.Length; i++) {
if (SpinnerIndications[i] == status) {
newStatusIsSpinner = true;
}
}
bool latestStatusIsSpinner = false;
for (var i = 0; i < SpinnerIndications.Length; i++) {
if (SpinnerIndications[i] == latestStatus) {
newStatusIsSpinner = true;
}
}
if (status == latestStatus) {
return status;
} else if (latestStatusIsSpinner) {
......
......@@ -143,7 +143,7 @@ namespace SingularityGroup.HotReload.Editor {
return null;
}
}
// Unity Define Constraints syntax is described in the docs https://docs.unity3d.com/Manual/class-AssemblyDefinitionImporter.html
static readonly Dictionary<string, string> syntaxMap = new Dictionary<string, string> {
{ "OR", "||" },
......@@ -151,6 +151,7 @@ namespace SingularityGroup.HotReload.Editor {
{ "NOT", "!" }
};
/// <summary>
/// Evaluate a define constraint like 'UNITY_ANDROID || UNITY_IOS'
/// </summary>
......
......@@ -71,6 +71,7 @@ namespace SingularityGroup.HotReload.Editor {
buildMachineHostName = hostname,
buildMachinePort = RequestHelper.port,
activeBuildTarget = input.activeBuildTarget.ToString(),
buildMachineRequestOrigin = RequestHelper.origin,
};
}
......
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor;
......@@ -17,8 +17,11 @@ namespace SingularityGroup.HotReload.Editor {
[Obsolete] SymbolicLinks,
AutoRecompiledWhenPlaymodeStateChanges,
UnityBestDevelopmentToolAward2023,
#if UNITY_2022_1_OR_NEWER
AutoRecompiledWhenPlaymodeStateChanges2022,
#endif
MultidimensionalArrays,
EditorsWithoutHRRunning,
}
internal static class HotReloadSuggestionsHelper {
......@@ -39,6 +42,20 @@ namespace SingularityGroup.HotReload.Editor {
return EditorPrefs.GetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}");
}
// used for cases where suggestion might need to be shown more than once
internal static void SetSuggestionActive(HotReloadSuggestionKind hotReloadSuggestionKind) {
if (EditorPrefs.GetBool($"HotReloadWindow.SuggestionsShown.{hotReloadSuggestionKind}")) {
return;
}
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", true);
AlertEntry entry;
if (suggestionMap.TryGetValue(hotReloadSuggestionKind, out entry) && !HotReloadTimelineHelper.Suggestions.Contains(entry)) {
HotReloadTimelineHelper.Suggestions.Insert(0, entry);
HotReloadState.ShowingRedDot = true;
}
}
internal static void SetSuggestionInactive(HotReloadSuggestionKind hotReloadSuggestionKind) {
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", false);
AlertEntry entry;
......@@ -70,6 +87,7 @@ namespace SingularityGroup.HotReload.Editor {
internal static readonly OpenURLButton recompileTroubleshootingButton = new OpenURLButton("Documentation", Constants.RecompileTroubleshootingURL);
internal static readonly OpenURLButton featuresDocumentationButton = new OpenURLButton("Documentation", Constants.FeaturesDocumentationURL);
internal static readonly OpenURLButton multipleEditorsDocumentationButton = new OpenURLButton("Documentation", Constants.MultipleEditorsURL);
public static Dictionary<HotReloadSuggestionKind, AlertEntry> suggestionMap = new Dictionary<HotReloadSuggestionKind, AlertEntry> {
{ HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023, new AlertEntry(
AlertType.Suggestion,
......@@ -164,7 +182,7 @@ namespace SingularityGroup.HotReload.Editor {
#endif
{ HotReloadSuggestionKind.MultidimensionalArrays, new AlertEntry(
AlertType.Suggestion,
"Multidimensional arrays are not supported. Use jagged arrays instead",
"Use jagged instead of multidimensional arrays",
"Hot Reload doesn't support multidimensional ([,]) arrays. Jagged arrays ([][]) are a better alternative, and Microsoft recommends using them instead",
iconType: AlertType.UnsupportedChange,
actionData: () => {
......@@ -179,6 +197,33 @@ namespace SingularityGroup.HotReload.Editor {
timestamp: DateTime.Now,
entryType: EntryType.Foldout
)},
{ HotReloadSuggestionKind.EditorsWithoutHRRunning, new AlertEntry(
AlertType.Suggestion,
"Some Unity instances don't have Hot Reload running.",
"Make sure that either: \n1) Hot Reload is installed and running on all Editor instances, or \n2) Hot Reload is stopped in all Editor instances where it is installed.",
actionData: () => {
GUILayout.Space(10f);
using (new EditorGUILayout.HorizontalScope()) {
if (GUILayout.Button(" Stop Hot Reload ")) {
EditorCodePatcher.StopCodePatcher().Forget();
}
GUILayout.Space(5f);
multipleEditorsDocumentationButton.OnGUI();
GUILayout.Space(5f);
if (GUILayout.Button(" Don't show again ")) {
HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.EditorsWithoutHRRunning);
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
}
GUILayout.FlexibleSpace();
GUILayout.FlexibleSpace();
}
},
timestamp: DateTime.Now,
entryType: EntryType.Foldout,
iconType: AlertType.UnsupportedChange
)},
};
static ListRequest listRequest;
......@@ -197,15 +242,23 @@ namespace SingularityGroup.HotReload.Editor {
};
CompilationPipeline.compilationStarted += obj => {
if (DateTime.UtcNow - lastPlaymodeChange < TimeSpan.FromSeconds(1) && !HotReloadState.RecompiledUnsupportedChangesOnExitPlaymode) {
#if UNITY_2022_1_OR_NEWER
SetSuggestionsShown(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022);
#else
SetSuggestionsShown(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges);
#endif
}
HotReloadState.RecompiledUnsupportedChangesOnExitPlaymode = false;
};
InitSuggestions();
}
private static DateTime lastCheckedUnityInstances = DateTime.UtcNow;
public static void Check() {
if (listRequest.IsCompleted && unsupportedPackagesList == null) {
if (listRequest.IsCompleted &&
unsupportedPackagesList == null)
{
unsupportedPackagesList = new List<string>();
var packages = listRequest.Result;
foreach (var packageInfo in packages) {
......@@ -217,6 +270,8 @@ namespace SingularityGroup.HotReload.Editor {
SetSuggestionsShown(HotReloadSuggestionKind.UnsupportedPackages);
}
}
CheckEditorsWithoutHR();
#if UNITY_2022_1_OR_NEWER
if (EditorSettings.spritePackerMode == SpritePackerMode.AlwaysOnAtlas) {
......@@ -227,5 +282,41 @@ namespace SingularityGroup.HotReload.Editor {
}
#endif
}
private static void CheckEditorsWithoutHR() {
if (!ServerHealthCheck.I.IsServerHealthy) {
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
return;
}
if (checkingEditorsWihtoutHR ||
(DateTime.UtcNow - lastCheckedUnityInstances).TotalSeconds < 5)
{
return;
}
CheckEditorsWithoutHRAsync().Forget();
}
static bool checkingEditorsWihtoutHR;
private static async Task CheckEditorsWithoutHRAsync() {
try {
checkingEditorsWihtoutHR = true;
var showSuggestion = await Task.Run(() => {
var runningUnities = Process.GetProcessesByName("Unity").Length;
var runningPatchers = Process.GetProcessesByName("CodePatcherCLI").Length;
return runningUnities > runningPatchers;
});
if (!showSuggestion) {
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
return;
}
if (!HotReloadState.ShowedEditorsWithoutHR && ServerHealthCheck.I.IsServerHealthy) {
HotReloadSuggestionsHelper.SetSuggestionActive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
HotReloadState.ShowedEditorsWithoutHR = true;
}
} finally {
checkingEditorsWihtoutHR = false;
lastCheckedUnityInstances = DateTime.UtcNow;
}
}
}
}
......@@ -11,12 +11,18 @@ namespace SingularityGroup.HotReload.Editor {
[Overlay(typeof(SceneView), "Hot Reload", true)]
[Icon("Assets/HotReload/Editor/Resources/Icon_DarkMode.png")]
internal class HotReloadOverlay : ToolbarOverlay {
HotReloadOverlay() : base(HotReloadToolbarIndicationButton.id, HotReloadToolbarEventsButton.id, HotReloadToolbarRecompileButton.id) {}
HotReloadOverlay() : base(HotReloadToolbarIndicationButton.id, HotReloadToolbarEventsButton.id, HotReloadToolbarRecompileButton.id) {
EditorApplication.update += Update;
}
EditorIndicationState.IndicationStatus lastIndicationStatus;
[EditorToolbarElement(id, typeof(SceneView))]
class HotReloadToolbarIndicationButton : EditorToolbarButton, IAccessContainerWindow {
internal const string id = "HotReloadOverlay/LogoButton";
public EditorWindow containerWindow { get; set; }
EditorIndicationState.IndicationStatus lastIndicationStatus;
internal HotReloadToolbarIndicationButton() {
icon = GetIndicationIcon();
......@@ -31,8 +37,11 @@ namespace SingularityGroup.HotReload.Editor {
}
void Update() {
icon = GetIndicationIcon();
tooltip = EditorIndicationState.IndicationStatusText;
if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
icon = GetIndicationIcon();
tooltip = EditorIndicationState.IndicationStatusText;
lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
}
}
~HotReloadToolbarIndicationButton() {
......@@ -46,6 +55,8 @@ namespace SingularityGroup.HotReload.Editor {
internal const string id = "HotReloadOverlay/EventsButton";
public EditorWindow containerWindow { get; set; }
bool lastShowingRedDot;
internal HotReloadToolbarEventsButton() {
icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
tooltip = "Events";
......@@ -58,7 +69,10 @@ namespace SingularityGroup.HotReload.Editor {
}
void Update() {
icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
if (lastShowingRedDot != HotReloadState.ShowingRedDot) {
icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
lastShowingRedDot = HotReloadState.ShowingRedDot;
}
}
~HotReloadToolbarEventsButton() {
......@@ -97,6 +111,7 @@ namespace SingularityGroup.HotReload.Editor {
private static Image indicationIcon;
private static Label indicationText;
bool initialized;
/// <summary>
/// Create Hot Reload overlay panel.
/// </summary>
......@@ -120,8 +135,7 @@ namespace SingularityGroup.HotReload.Editor {
root.Add(indicationText);
root.style.width = 190;
root.style.height = 32;
EditorApplication.update += Update;
initialized = true;
return root;
}
......@@ -129,12 +143,18 @@ namespace SingularityGroup.HotReload.Editor {
static bool _instantRepaint;
static DateTime _lastRepaint;
private void Update() {
indicationIcon.image = GetIndicationIcon();
indicationText.text = EditorIndicationState.IndicationStatusText;
if (!initialized) {
return;
}
if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
indicationIcon.image = GetIndicationIcon();
indicationText.text = EditorIndicationState.IndicationStatusText;
lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
}
try {
if (EditorWindow.mouseOverWindow
if (HotReloadEventPopup.I.open
&& EditorWindow.mouseOverWindow
&& EditorWindow.mouseOverWindow?.GetType() == typeof(UnityEditor.PopupWindow)
&& HotReloadEventPopup.I.open
) {
_repaint = true;
}
......@@ -149,6 +169,10 @@ namespace SingularityGroup.HotReload.Editor {
HotReloadEventPopup.I.Repaint();
}
}
~HotReloadOverlay() {
EditorApplication.update -= Update;
}
}
}
#endif
......@@ -35,6 +35,7 @@ namespace SingularityGroup.HotReload.Editor {
private const string LaunchOnEditorStartKey = "HotReloadWindow.LaunchOnEditorStart";
private const string AutoRecompileUnsupportedChangesKey = "HotReloadWindow.AutoRecompileUnsupportedChanges";
private const string AutoRecompilePartiallyUnsupportedChangesKey = "HotReloadWindow.AutoRecompilePartiallyUnsupportedChanges";
private const string ShowNotificationsKey = "HotReloadWindow.ShowNotifications";
private const string ShowPatchingNotificationsKey = "HotReloadWindow.ShowPatchingNotifications";
private const string ShowCompilingUnsupportedNotificationsKey = "HotReloadWindow.ShowCompilingUnsupportedNotifications";
private const string AutoRecompileUnsupportedChangesImmediatelyKey = "HotReloadWindow.AutoRecompileUnsupportedChangesImmediately";
......@@ -270,6 +271,11 @@ namespace SingularityGroup.HotReload.Editor {
set { EditorPrefs.SetBool(AutoRecompilePartiallyUnsupportedChangesKey, value); }
}
public static bool ShowNotifications {
get { return EditorPrefs.GetBool(ShowNotificationsKey, true); }
set { EditorPrefs.SetBool(ShowNotificationsKey, value); }
}
public static bool ShowPatchingNotifications {
get { return EditorPrefs.GetBool(ShowPatchingNotificationsKey, true); }
set { EditorPrefs.SetBool(ShowPatchingNotificationsKey, value); }
......
......@@ -2,9 +2,16 @@ using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
internal static class HotReloadState {
private const string ServerPortKey = "HotReloadWindow.ServerPort";
private const string LastPatchIdKey = "HotReloadWindow.LastPatchId";
private const string ShowingRedDotKey = "HotReloadWindow.ShowingRedDot";
private const string ShowedEditorsWithoutHRKey = "HotReloadWindow.ShowedEditorWithoutHR";
private const string RecompiledUnsupportedChangesOnExitPlaymodeKey = "HotReloadWindow.RecompiledUnsupportedChangesOnExitPlaymode";
public static int ServerPort {
get { return SessionState.GetInt(ServerPortKey, RequestHelper.defaultPort); }
set { SessionState.SetInt(ServerPortKey, value); }
}
public static string LastPatchId {
get { return SessionState.GetString(LastPatchIdKey, string.Empty); }
......@@ -16,6 +23,11 @@ namespace SingularityGroup.HotReload.Editor {
set { SessionState.SetBool(ShowingRedDotKey, value); }
}
public static bool ShowedEditorsWithoutHR {
get { return SessionState.GetBool(ShowedEditorsWithoutHRKey, false); }
set { SessionState.SetBool(ShowedEditorsWithoutHRKey, value); }
}
public static bool RecompiledUnsupportedChangesOnExitPlaymode {
get { return SessionState.GetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, false); }
set { SessionState.SetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, value); }
......
using System.Reflection;
using SingularityGroup.HotReload.Editor;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public class InspectorFreezeFix
{
// Inspector window getting stuck is fixed by calling UnityEditor.InspectorWindow.RefreshInspectors()
// Below code subscribes to selection changed callback and calls the method if the inspector is actually stuck
static InspectorFreezeFix()
{
Selection.selectionChanged += OnSelectionChanged;
}
private static int _lastInitialEditorId;
private static void OnSelectionChanged() {
if (!EditorCodePatcher.config.enableInspectorFreezeFix) {
return;
}
try {
// Most of stuff is internal so we use reflection here
var inspectorType = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow");
foreach (var inspector in Resources.FindObjectsOfTypeAll(inspectorType)) {
object isLockedValue = inspectorType.GetProperty("isLocked")?.GetValue(inspector);
if (isLockedValue == null) {
continue;
}
// If inspector window is locked deliberately by user (via the lock icon on top-right), we don't need to refresh
var isLocked = (bool)isLockedValue;
if (isLocked) {
continue;
}
// Inspector getting stuck is due to ActiveEditorTracker of that window getting stuck internally.
// The tracker starts returning same values from m_Tracker.activeEditors property.
// (Root of cause of this is out of my reach as the tracker code is mainly native code)
// We detect that by checking first element of activeEditors array
// We do the check because we don't want to RefreshInspectors every selection change, RefreshInspectors is expensive
var tracker = inspectorType.GetField("m_Tracker", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(inspector);
if (tracker == null) {
continue;
}
var activeEditors = tracker.GetType().GetProperty("activeEditors");
if (activeEditors == null) {
continue;
}
var editors = (Editor[])activeEditors.GetValue(tracker);
if (editors.Length == 0) {
continue;
}
var first = editors[0].GetInstanceID();
if (_lastInitialEditorId == first) {
// This forces the tracker to be rebuilt
var m = inspectorType.GetMethod("RefreshInspectors", BindingFlags.Static | BindingFlags.NonPublic);
if (m == null) {
// support for older versions
RefreshInspectors(inspectorType);
} else {
m.Invoke(null, null);
}
}
_lastInitialEditorId = first;
// Calling RefreshInspectors once refreshes all the editors
break;
}
} catch {
// ignore, we don't want to make user experience worse by displaying a warning in this case
}
}
static void RefreshInspectors(System.Type inspectorType) {
var allInspectorsField = inspectorType.GetField("m_AllInspectors", BindingFlags.NonPublic | BindingFlags.Static);
if (allInspectorsField == null) {
return;
}
var allInspectors = allInspectorsField.GetValue(null) as System.Collections.IEnumerable;
if (allInspectors == null) {
return;
}
foreach (var inspector in allInspectors) {
var trackerField = FindFieldInHierarchy(inspector.GetType(), "tracker");
if (trackerField == null) {
continue;
}
var tracker = trackerField.GetValue(inspector);
var forceRebuildMethod = tracker.GetType().GetMethod("ForceRebuild", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (forceRebuildMethod == null) {
continue;
}
forceRebuildMethod.Invoke(tracker, null);
}
}
static PropertyInfo FindFieldInHierarchy(System.Type type, string fieldName) {
PropertyInfo field = null;
while (type != null && field == null) {
field = type.GetProperty(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
type = type.BaseType;
}
return field;
}
}
fileFormatVersion: 2
guid: 235343744f6348acb629d549ccafff0b
timeCreated: 1708187279
\ No newline at end of file
......@@ -13,7 +13,7 @@ namespace SingularityGroup.HotReload.Editor {
public static async Task<DownloadResult> DownloadFile(string url, string targetFilePath, IProgress<float> progress, CancellationToken cancellationToken) {
var tmpDir = Path.GetDirectoryName(targetFilePath);
Directory.CreateDirectory(tmpDir);
using(var client = RequestHelper.CreateHttpClient()) {
using(var client = HttpClientUtils.CreateHttpClient()) {
client.Timeout = TimeSpan.FromMinutes(10);
return await client.DownloadAsync(url, targetFilePath, progress, cancellationToken).ConfigureAwait(false);
}
......@@ -73,7 +73,7 @@ namespace SingularityGroup.HotReload.Editor {
throw new ArgumentException("Has to be writable", nameof(destination));
if (bufferSize < 0)
throw new ArgumentOutOfRangeException(nameof(bufferSize));
var buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
......
No preview for this file type
......@@ -21,7 +21,7 @@ namespace SingularityGroup.HotReload.Editor {
private static TimeSpan RetryInterval => TimeSpan.FromSeconds(30);
private static TimeSpan CheckInterval => TimeSpan.FromHours(1);
private static readonly HttpClient client = RequestHelper.CreateHttpClient();
private static readonly HttpClient client = HttpClientUtils.CreateHttpClient();
private static string _lastRemotePackageVersion;
......@@ -45,7 +45,7 @@ namespace SingularityGroup.HotReload.Editor {
await Task.Delay(RetryInterval);
}
}
public bool TryGetNewVersion(out SemVersion version) {
var currentVersion = SemVersion.Parse(PackageConst.Version, strict: true);
return !ReferenceEquals(version = newVersionDetected, null) && newVersionDetected > currentVersion;
......
......@@ -62,18 +62,6 @@ namespace SingularityGroup.HotReload.Editor {
}
}, token);
}
void RunTask(Func<Task> action) {
var token = HotReloadWindow.Current.cancelToken;
Task.Run(async () => {
if (token.IsCancellationRequested) return;
try {
await action();
} catch (Exception ex) {
ThreadUtility.LogException(ex, token);
}
}, token);
}
void RunOnMainThreadSync(Action action) {
ThreadUtility.RunOnMainThread(action, HotReloadWindow.Current.cancelToken);
......
......@@ -214,7 +214,7 @@ namespace SingularityGroup.HotReload.Editor {
requestingRedeem = true;
await ThreadUtility.SwitchToThreadPool();
try {
redeemClient = redeemClient ?? (redeemClient = RequestHelper.CreateHttpClient());
redeemClient = redeemClient ?? (redeemClient = HttpClientUtils.CreateHttpClient());
var input = new Dictionary<string, string> {
{ "email", email },
{ "invoice", invoiceNumber }
......
......@@ -8,7 +8,6 @@ using UnityEditor;
using UnityEngine;
using System.Threading.Tasks;
using System.IO;
using SingularityGroup.HotReload.Editor.Cli;
using SingularityGroup.HotReload.Newtonsoft.Json;
using SingularityGroup.HotReload.EditorDependencies;
......@@ -48,7 +47,7 @@ namespace SingularityGroup.HotReload.Editor {
private bool _requestedChangelog;
private int _changelogRequestAttempt;
private string _changelogDir = Path.Combine(PackageConst.LibraryCachePath, "changelog.json");
public static string logsPath = Path.Combine(CliUtils.GetAppDataPath(), "logs");
public static string logsPath = Path.Combine(PackageConst.LibraryCachePath, "logs");
private static bool LatestChangelogLoaded(IReadOnlyList<ChangelogVersion> changelog) {
return changelog.Any() && changelog[0].versionNum == PackageUpdateChecker.lastRemotePackageVersion;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment