[Add] Hot Reload
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5dfa6492e8e7ce4f937aa75ef4e86fd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ae8b0adf00c450d9e80e11ffa1d2cf7
|
||||
timeCreated: 1678721517
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.VSAttribution.HotReload;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class Attribution {
|
||||
internal const string LastLoginKey = "HotReload.Attribution.LastAttributionEventAt";
|
||||
|
||||
//Resend attribution event every 12 hours to be safe
|
||||
static readonly TimeSpan resendPeriod = TimeSpan.FromHours(12);
|
||||
|
||||
//The last time the attribution event was sent.
|
||||
//Returns unix epoch in case it has never been sent before.
|
||||
static DateTime LastAttributionEventAt {
|
||||
get {
|
||||
if(EditorPrefs.HasKey(LastLoginKey)) {
|
||||
return DateTime.ParseExact(EditorPrefs.GetString(LastLoginKey), "o", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
}
|
||||
return DateTimeOffset.FromUnixTimeSeconds(0).UtcDateTime;
|
||||
}
|
||||
set {
|
||||
EditorPrefs.SetString(LastLoginKey, value.ToUniversalTime().ToString("o"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const string actionName = "Login";
|
||||
const string partnerName = "The Naughty Cult Ltd.";
|
||||
|
||||
public static void RegisterLogin(LoginStatusResponse response) {
|
||||
//Licensing might not be initialized yet.
|
||||
//The hwId should be set eventually.
|
||||
if(response?.hardwareId == null) {
|
||||
return;
|
||||
}
|
||||
//Only forward attribution if this is an asset store build.
|
||||
//We will still distribute this package outside of the asset store (i.e via our website).
|
||||
if (!PackageConst.IsAssetStoreBuild) {
|
||||
return;
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
//If we sent an attribution event in the last 12 hours we should already be good.
|
||||
if (now - LastAttributionEventAt < resendPeriod) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = VSAttribution.SendAttributionEvent(actionName, partnerName, response.hardwareId);
|
||||
|
||||
//Retry on transient errors
|
||||
if (result == AnalyticsResult.NotInitialized) {
|
||||
return;
|
||||
}
|
||||
LastAttributionEventAt = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67658aafb8404f0eb9496812ba4bb8a4
|
||||
timeCreated: 1678721795
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using UnityEngine.Analytics;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
namespace UnityEditor.VSAttribution.HotReload
|
||||
{
|
||||
internal static class VSAttribution
|
||||
{
|
||||
|
||||
const int k_VersionId = 4;
|
||||
const int k_MaxEventsPerHour = 10;
|
||||
const int k_MaxNumberOfElements = 1000;
|
||||
|
||||
const string k_VendorKey = "unity.vsp-attribution";
|
||||
const string k_EventName = "vspAttribution";
|
||||
|
||||
[Serializable]
|
||||
private class VSAttributionData : IAnalytic.IData {
|
||||
public string actionName;
|
||||
public string partnerName;
|
||||
public string customerUid;
|
||||
public string extra;
|
||||
}
|
||||
|
||||
[AnalyticInfo(k_EventName, k_VendorKey, k_VersionId, k_MaxEventsPerHour, k_MaxNumberOfElements)]
|
||||
class VSAttributionAnalytics : IAnalytic {
|
||||
private VSAttributionData m_Data;
|
||||
|
||||
private VSAttributionAnalytics(
|
||||
string actionName,
|
||||
string partnerName,
|
||||
string customerUid,
|
||||
string extra
|
||||
) {
|
||||
this.m_Data = new VSAttributionData {
|
||||
actionName = actionName,
|
||||
partnerName = partnerName,
|
||||
customerUid = customerUid,
|
||||
extra = extra
|
||||
};
|
||||
}
|
||||
|
||||
public bool TryGatherData(out IAnalytic.IData data, out Exception error) {
|
||||
data = this.m_Data;
|
||||
error = null;
|
||||
return this.m_Data != null;
|
||||
}
|
||||
|
||||
public static AnalyticsResult SendEvent(
|
||||
string actionName,
|
||||
string partnerName,
|
||||
string customerUid,
|
||||
string extra
|
||||
) {
|
||||
return EditorAnalytics.SendAnalytic(new VSAttributionAnalytics(actionName,
|
||||
partnerName,
|
||||
customerUid,
|
||||
extra
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers and attempts to send a Verified Solutions Attribution event.
|
||||
/// </summary>
|
||||
/// <param name="actionName">Name of the action, identifying a place this event was called from.</param>
|
||||
/// <param name="partnerName">Identifiable Verified Solutions Partner's name.</param>
|
||||
/// <param name="customerUid">Unique identifier of the customer using Partner's Verified Solution.</param>
|
||||
public static AnalyticsResult SendAttributionEvent(string actionName, string partnerName, string customerUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
return VSAttributionAnalytics.SendEvent(actionName, partnerName, customerUid, "{}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fail silently
|
||||
return AnalyticsResult.AnalyticsDisabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
namespace UnityEditor.VSAttribution.HotReload
|
||||
{
|
||||
internal static class VSAttribution
|
||||
{
|
||||
const int k_VersionId = 4;
|
||||
const int k_MaxEventsPerHour = 10;
|
||||
const int k_MaxNumberOfElements = 1000;
|
||||
|
||||
const string k_VendorKey = "unity.vsp-attribution";
|
||||
const string k_EventName = "vspAttribution";
|
||||
|
||||
static bool RegisterEvent()
|
||||
{
|
||||
AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_EventName, k_MaxEventsPerHour,
|
||||
k_MaxNumberOfElements, k_VendorKey, k_VersionId);
|
||||
|
||||
var isResultOk = result == AnalyticsResult.Ok;
|
||||
return isResultOk;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
struct VSAttributionData
|
||||
{
|
||||
public string actionName;
|
||||
public string partnerName;
|
||||
public string customerUid;
|
||||
public string extra;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers and attempts to send a Verified Solutions Attribution event.
|
||||
/// </summary>
|
||||
/// <param name="actionName">Name of the action, identifying a place this event was called from.</param>
|
||||
/// <param name="partnerName">Identifiable Verified Solutions Partner's name.</param>
|
||||
/// <param name="customerUid">Unique identifier of the customer using Partner's Verified Solution.</param>
|
||||
public static AnalyticsResult SendAttributionEvent(string actionName, string partnerName, string customerUid)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Are Editor Analytics enabled ? (Preferences)
|
||||
if (!EditorAnalytics.enabled)
|
||||
return AnalyticsResult.AnalyticsDisabled;
|
||||
|
||||
if (!RegisterEvent())
|
||||
return AnalyticsResult.InvalidData;
|
||||
|
||||
// Create an expected data object
|
||||
var eventData = new VSAttributionData
|
||||
{
|
||||
actionName = actionName,
|
||||
partnerName = partnerName,
|
||||
customerUid = customerUid,
|
||||
extra = "{}"
|
||||
};
|
||||
|
||||
return EditorAnalytics.SendEventWithLimit(k_EventName, eventData, k_VersionId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fail silently
|
||||
return AnalyticsResult.AnalyticsDisabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7493a30e78d4ec783ead20baea2c4d2
|
||||
timeCreated: 1678721534
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a100625513d043c7bb875461043f4f86
|
||||
timeCreated: 1673820086
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
internal static class CliUtils {
|
||||
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)
|
||||
return Path.GetFullPath(PackageConst.LibraryCachePath + $"/HotReloadServerTemp/{osxFileName}");
|
||||
} else {
|
||||
return Path.GetTempFileName();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetHotReloadTempDir() {
|
||||
return Path.GetFullPath(Path.Combine(PackageConst.LibraryCachePath, "HotReloadServerTemp"));
|
||||
}
|
||||
|
||||
public static string GetAppDataPath() {
|
||||
# if (UNITY_EDITOR_OSX)
|
||||
var baseDir = "/Users/Shared";
|
||||
# elif (UNITY_EDITOR_LINUX)
|
||||
var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
# else
|
||||
var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
#endif
|
||||
return Path.Combine(baseDir, "singularitygroup-hotreload");
|
||||
}
|
||||
|
||||
public static string GetExecutableTargetDir() {
|
||||
if (PackageConst.IsAssetStoreBuild) {
|
||||
if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) {
|
||||
return Path.Combine(GetAppDataPath(), "asset-store", "zh", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
|
||||
}
|
||||
return Path.Combine(GetAppDataPath(), "asset-store", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
|
||||
}
|
||||
if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) {
|
||||
return Path.Combine(GetAppDataPath(), "zh", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
|
||||
}
|
||||
return Path.Combine(GetAppDataPath(), $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
|
||||
}
|
||||
|
||||
public static string GetCliTempDir() {
|
||||
return Path.Combine(GetHotReloadTempDir(), "MethodPatches");
|
||||
}
|
||||
|
||||
public static void Chmod(string targetFile, string flags = "+x") {
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
Process.Start(new ProcessStartInfo("chmod", $"{flags} \"{targetFile}\"") {
|
||||
UseShellExecute = false,
|
||||
}).WaitForExit(2000);
|
||||
}
|
||||
|
||||
public static bool TryFindServerDir(out string path) {
|
||||
const string serverBasePath = "Packages/com.singularitygroup.hotreload/Server";
|
||||
if(Directory.Exists(serverBasePath)) {
|
||||
path = Path.GetFullPath(serverBasePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Not found in packages. Try to find in assets folder.
|
||||
//fast path - this is the expected folder
|
||||
const string alternativeExecutablePath = "Assets/HotReload/Server";
|
||||
if(Directory.Exists(alternativeExecutablePath)) {
|
||||
path = Path.GetFullPath(alternativeExecutablePath);
|
||||
return true;
|
||||
}
|
||||
//slow path - try to find the server directory somewhere in the assets folder
|
||||
var candidates = Directory.GetDirectories("Assets", "HotReload", SearchOption.AllDirectories);
|
||||
foreach(var candidate in candidates) {
|
||||
var serverDir = Path.Combine(candidate, "Server");
|
||||
if(Directory.Exists(serverDir)) {
|
||||
path = Path.GetFullPath(serverDir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string GetPidFilePath(string hotreloadTempDir) {
|
||||
return Path.GetFullPath(Path.Combine(hotreloadTempDir, "server.pid"));
|
||||
}
|
||||
|
||||
public static void KillLastKnownHotReloadProcess() {
|
||||
var pidPath = GetPidFilePath(GetHotReloadTempDir());
|
||||
try {
|
||||
var pid = int.Parse(File.ReadAllText(pidPath));
|
||||
Process.GetProcessById(pid).Kill();
|
||||
}
|
||||
catch {
|
||||
//ignore
|
||||
}
|
||||
File.Delete(pidPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0243b348dec4a308dc7b98e09842d2c
|
||||
timeCreated: 1673820875
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
class FallbackCliController : ICliController {
|
||||
public string BinaryFileName => "";
|
||||
public string PlatformName => "";
|
||||
public bool CanOpenInBackground => false;
|
||||
public Task Start(StartArgs args) => Task.CompletedTask;
|
||||
|
||||
public Task Stop() => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 090ed5d45f294f0d8799879206139bd6
|
||||
timeCreated: 1673824275
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
#if UNITY_EDITOR_WIN
|
||||
using System.Net.NetworkInformation;
|
||||
#else
|
||||
using System.Net.Sockets;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
[InitializeOnLoad]
|
||||
public static class HotReloadCli {
|
||||
internal static readonly ICliController controller;
|
||||
|
||||
//InitializeOnLoad ensures controller gets initialized on unity thread
|
||||
static HotReloadCli() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
controller = new FallbackCliController();
|
||||
return;
|
||||
}
|
||||
controller =
|
||||
#if UNITY_EDITOR_OSX
|
||||
new OsxCliController();
|
||||
#elif UNITY_EDITOR_LINUX
|
||||
new LinuxCliController();
|
||||
#elif UNITY_EDITOR_WIN
|
||||
new WindowsCliController();
|
||||
#else
|
||||
new FallbackCliController();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool CanOpenInBackground => controller.CanOpenInBackground;
|
||||
|
||||
/// <summary>
|
||||
/// Public API: Starts the Hot Reload server. Must be on the main thread
|
||||
/// </summary>
|
||||
public static Task StartAsync() {
|
||||
return StartAsync(
|
||||
isReleaseMode: RequestHelper.IsReleaseMode(),
|
||||
exposeServerToNetwork: HotReloadPrefs.ExposeServerToLocalNetwork,
|
||||
allAssetChanges: HotReloadPrefs.AllAssetChanges,
|
||||
createNoWindow: HotReloadPrefs.DisableConsoleWindow,
|
||||
#if UNITY_EDITOR_WIN
|
||||
useWatchman: HotReloadPrefs.UseWatchman,
|
||||
#endif
|
||||
detailedErrorReporting: !HotReloadPrefs.DisableDetailedErrorReporting
|
||||
);
|
||||
}
|
||||
|
||||
internal static async Task StartAsync(
|
||||
bool exposeServerToNetwork,
|
||||
bool allAssetChanges,
|
||||
bool createNoWindow,
|
||||
bool isReleaseMode,
|
||||
bool detailedErrorReporting,
|
||||
#if UNITY_EDITOR_WIN
|
||||
bool useWatchman = true,
|
||||
#endif
|
||||
LoginData loginData = null
|
||||
) {
|
||||
if (controller.PlatformName == "") {
|
||||
return;
|
||||
}
|
||||
var port = await Prepare().ConfigureAwait(false);
|
||||
await ThreadUtility.SwitchToThreadPool();
|
||||
StartArgs args;
|
||||
if (TryGetStartArgs(
|
||||
UnityHelper.DataPath,
|
||||
exposeServerToNetwork,
|
||||
allAssetChanges,
|
||||
createNoWindow,
|
||||
isReleaseMode,
|
||||
detailedErrorReporting,
|
||||
loginData,
|
||||
port,
|
||||
#if UNITY_EDITOR_WIN
|
||||
useWatchman,
|
||||
#endif
|
||||
out args)) {
|
||||
await controller.Start(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public API: Stops the Hot Reload server
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a no-op in case the server is not running
|
||||
/// </remarks>
|
||||
public static Task StopAsync() {
|
||||
return controller.Stop();
|
||||
}
|
||||
|
||||
class Config {
|
||||
#pragma warning disable CS0649
|
||||
public bool useBuiltInProjectGeneration;
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
|
||||
static bool TryGetStartArgs(
|
||||
string dataPath,
|
||||
bool exposeServerToNetwork,
|
||||
bool allAssetChanges,
|
||||
bool createNoWindow,
|
||||
bool isReleaseMode,
|
||||
bool detailedErrorReporting,
|
||||
LoginData loginData,
|
||||
int port,
|
||||
#if UNITY_EDITOR_WIN
|
||||
bool useWatchman,
|
||||
#endif
|
||||
out StartArgs args
|
||||
) {
|
||||
string serverDir;
|
||||
if(!CliUtils.TryFindServerDir(out serverDir)) {
|
||||
Log.Warning(string.Format(Translations.Errors.WarningFailedToStartServer,
|
||||
Translations.Utility.UnableToLocateServer +
|
||||
Translations.Utility.UnableToLocateServerDetail));
|
||||
args = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
Config config;
|
||||
if (File.Exists(PackageConst.ConfigFileName)) {
|
||||
config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFileName));
|
||||
} else {
|
||||
config = new Config();
|
||||
}
|
||||
var hotReloadTmpDir = CliUtils.GetHotReloadTempDir();
|
||||
var cliTempDir = CliUtils.GetCliTempDir();
|
||||
// Versioned path so that we only need to extract the binary once. User can have multiple projects
|
||||
// on their machine using different HotReload versions.
|
||||
var executableTargetDir = CliUtils.GetExecutableTargetDir();
|
||||
Directory.CreateDirectory(executableTargetDir); // ensure exists
|
||||
var executableSourceDir = Path.Combine(serverDir, controller.PlatformName);
|
||||
var unityProjDir = Path.GetDirectoryName(dataPath);
|
||||
string slnPath;
|
||||
if (config.useBuiltInProjectGeneration) {
|
||||
var info = new DirectoryInfo(Path.GetFullPath("."));
|
||||
slnPath = Path.Combine(Path.GetFullPath("."), info.Name + ".sln");
|
||||
if (!File.Exists(slnPath)) {
|
||||
Log.Warning(string.Format(Translations.Errors.WarningFailedToStartServer, Translations.Utility.CannotFindSolutionFile));
|
||||
args = null;
|
||||
return false;
|
||||
}
|
||||
Log.Info(Translations.Errors.InfoDefaultProjectGeneration);
|
||||
try {
|
||||
Directory.Delete(ProjectGeneration.ProjectGeneration.tempDir, true);
|
||||
} catch(Exception ex) {
|
||||
Log.Exception(ex);
|
||||
}
|
||||
} else {
|
||||
slnPath = ProjectGeneration.ProjectGeneration.GetSolutionFilePath(dataPath);
|
||||
}
|
||||
|
||||
if (!File.Exists(slnPath)) {
|
||||
Log.Warning(Translations.Errors.WarningNoSlnFileFound);
|
||||
}
|
||||
|
||||
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}"" -p ""{port}"" -r {isReleaseMode} -detailed-error-reporting {detailedErrorReporting}";
|
||||
if (loginData != null) {
|
||||
cliArguments += $@" -email ""{loginData.email}"" -pass ""{loginData.password}""";
|
||||
}
|
||||
#if UNITY_EDITOR_WIN
|
||||
cliArguments += $@" -w ""{useWatchman}""";
|
||||
#endif
|
||||
if (exposeServerToNetwork) {
|
||||
// server will listen on local network interface (default is localhost only)
|
||||
cliArguments += " -e true";
|
||||
}
|
||||
args = new StartArgs {
|
||||
hotreloadTempDir = hotReloadTmpDir,
|
||||
cliTempDir = cliTempDir,
|
||||
executableTargetDir = executableTargetDir,
|
||||
executableSourceDir = executableSourceDir,
|
||||
cliArguments = cliArguments,
|
||||
unityProjDir = unityProjDir,
|
||||
createNoWindow = createNoWindow,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
var serverInfo = RequestHelper.SetServerPort(port);
|
||||
await Task.Run(() => File.WriteAllText(Path.Combine(PackageConst.LibraryCachePath, PackageConst.ServerInfoFileName), JsonConvert.SerializeObject(serverInfo)));
|
||||
return port;
|
||||
}
|
||||
|
||||
static bool didLogWarning;
|
||||
internal static async Task PrepareBuildInfoAsync() {
|
||||
await ThreadUtility.SwitchToMainThread();
|
||||
var buildInfoInput = await BuildInfoHelper.GetGenerateBuildInfoInput();
|
||||
await Task.Run(() => {
|
||||
try {
|
||||
var buildInfo = BuildInfoHelper.GenerateBuildInfoThreaded(buildInfoInput);
|
||||
PrepareBuildInfo(buildInfo);
|
||||
} catch (Exception e) {
|
||||
if (!didLogWarning) {
|
||||
Log.Warning(string.Format(Translations.Errors.WarningPreparingBuildInfoFailed, e));
|
||||
didLogWarning = true;
|
||||
} else {
|
||||
Log.Debug(string.Format(Translations.Utility.PreparingBuildInfoFailed, e));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal static void PrepareBuildInfo(BuildInfo buildInfo) {
|
||||
// When starting server make sure it starts with correct player data state.
|
||||
// (this fixes issue where Unity is in background and not sending files state).
|
||||
// Always write player data because you can be on any build target and want to connect with a downloaded android build.
|
||||
var json = buildInfo.ToJson();
|
||||
var cliTempDir = CliUtils.GetCliTempDir();
|
||||
Directory.CreateDirectory(cliTempDir);
|
||||
File.WriteAllText(Path.Combine(cliTempDir, "playerdata.json"), json);
|
||||
}
|
||||
|
||||
static void PrepareSystemPathsFile() {
|
||||
#pragma warning disable CS0618 // obsolete since 2023
|
||||
var lvl = PlayerSettings.GetApiCompatibilityLevel(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
#pragma warning restore CS0618
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
var dirs = UnityEditor.Compilation.CompilationPipeline.GetSystemAssemblyDirectories(lvl);
|
||||
#else
|
||||
var t = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.Scripting.ScriptCompilation.MonoLibraryHelpers");
|
||||
var m = t.GetMethod("GetSystemReferenceDirectories");
|
||||
var dirs = m.Invoke(null, new object[] { lvl });
|
||||
#endif
|
||||
Directory.CreateDirectory(PackageConst.LibraryCachePath);
|
||||
File.WriteAllText(PackageConst.LibraryCachePath + "/systemAssemblies.json", JsonConvert.SerializeObject(dirs));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f756ed6b78d428b8b9f83a6544317fe
|
||||
timeCreated: 1673820326
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
interface ICliController {
|
||||
string BinaryFileName {get;}
|
||||
string PlatformName {get;}
|
||||
bool CanOpenInBackground {get;}
|
||||
|
||||
Task Start(StartArgs args);
|
||||
|
||||
Task Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cba48e21f76483da3ba615915e731fd
|
||||
timeCreated: 1673820542
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
|
||||
class LinuxCliController : ICliController {
|
||||
Process process;
|
||||
|
||||
public string BinaryFileName => "CodePatcherCLI";
|
||||
public string PlatformName => "linux-x64";
|
||||
public bool CanOpenInBackground => true;
|
||||
|
||||
public Task Start(StartArgs args) {
|
||||
var startScript = Path.Combine(args.executableSourceDir, "hotreload-start-script.sh");
|
||||
if (!File.Exists(startScript)) {
|
||||
throw new FileNotFoundException(startScript);
|
||||
}
|
||||
File.WriteAllText(startScript, File.ReadAllText(startScript).Replace("\r\n", "\n"));
|
||||
CliUtils.Chmod(startScript);
|
||||
|
||||
var title = CodePatcher.TAG + "Server " + new DirectoryInfo(args.unityProjDir).Name;
|
||||
title = title.Replace(" ", "-");
|
||||
title = title.Replace("'", "");
|
||||
|
||||
var cliargsfile = Path.GetTempFileName();
|
||||
File.WriteAllText(cliargsfile,args.cliArguments);
|
||||
var codePatcherProc = Process.Start(new ProcessStartInfo {
|
||||
FileName = startScript,
|
||||
Arguments =
|
||||
$"--title \"{title}\""
|
||||
+ $" --executables-source-dir \"{args.executableSourceDir}\" "
|
||||
+ $" --executable-taget-dir \"{args.executableTargetDir}\""
|
||||
+ $" --pidfile \"{CliUtils.GetPidFilePath(args.hotreloadTempDir)}\""
|
||||
+ $" --cli-arguments-file \"{cliargsfile}\""
|
||||
+ $" --method-patch-dir \"{args.cliTempDir}\""
|
||||
+ $" --create-no-window \"{args.createNoWindow}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
});
|
||||
if (codePatcherProc == null) {
|
||||
if (File.Exists(cliargsfile)) {
|
||||
File.Delete(cliargsfile);
|
||||
}
|
||||
throw new Exception(Translations.Errors.ExceptionCouldNotStartCodePatcher);
|
||||
}
|
||||
codePatcherProc.BeginErrorReadLine();
|
||||
codePatcherProc.BeginOutputReadLine();
|
||||
codePatcherProc.OutputDataReceived += (_, a) => {
|
||||
};
|
||||
// error data can also mean we kill the proc beningly
|
||||
codePatcherProc.ErrorDataReceived += (_, a) => {
|
||||
};
|
||||
process = codePatcherProc;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Stop() {
|
||||
await RequestHelper.KillServer();
|
||||
try {
|
||||
// process.CloseMainWindow throws if proc already exited.
|
||||
// also we just rely on the pid file it is fine
|
||||
CliUtils.KillLastKnownHotReloadProcess();
|
||||
} catch {
|
||||
//ignored
|
||||
}
|
||||
process = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c894a69d595d4ada8cfa4afe23c68ab9
|
||||
timeCreated: 1673820131
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.Editor.Semver;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
class OsxCliController : ICliController {
|
||||
Process process;
|
||||
|
||||
public string BinaryFileName => "HotReload.app.zip";
|
||||
public string PlatformName => "osx-x64";
|
||||
public bool CanOpenInBackground => true;
|
||||
|
||||
/// In MacOS 13 Ventura, our app cannot launch a terminal window.
|
||||
/// We use a custom app that launches HotReload server and shows it's output (just like a terminal would).
|
||||
// Including MacOS 12 Monterey as well so I can dogfood it -Troy
|
||||
private static bool UseCustomConsoleApp() => MacOSVersion.Value.Major >= 12;
|
||||
|
||||
// dont use static because null comparison on SemVersion is broken
|
||||
private static readonly Lazy<SemVersion> MacOSVersion = new Lazy<SemVersion>(() => {
|
||||
//UnityHelper.OperatingSystem; // in Unity 2018 it returns 10.16 on monterey (no idea why)
|
||||
//Environment.OSVersion returns unix version like 21.x
|
||||
var startinfo = new ProcessStartInfo {
|
||||
FileName = "/usr/bin/sw_vers",
|
||||
Arguments = "-productVersion",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
var process = Process.Start(startinfo);
|
||||
|
||||
string osVersion = process.StandardOutput.ReadToEnd().Trim();
|
||||
|
||||
SemVersion macosVersion;
|
||||
if (SemVersion.TryParse(osVersion, out macosVersion)) {
|
||||
return macosVersion;
|
||||
}
|
||||
// should never happen
|
||||
Log.Warning(Translations.Errors.WarningMacOSVersionDetectionFailed);
|
||||
return SemVersion.None;
|
||||
});
|
||||
|
||||
public async Task Start(StartArgs args) {
|
||||
// Unzip the .app.zip to temp folder .app
|
||||
var appExecutablePath = $"{args.executableTargetDir}/HotReload.app/Contents/MacOS/HotReload";
|
||||
var cliExecutablePath = $"{args.executableTargetDir}/HotReload.app/Contents/Resources/CodePatcherCLI";
|
||||
|
||||
// ensure running on threadpool
|
||||
await ThreadUtility.SwitchToThreadPool();
|
||||
|
||||
// executableTargetDir is versioned, so only need to extract once.
|
||||
if (!File.Exists(appExecutablePath)) {
|
||||
try {
|
||||
// delete only the extracted app folder (must not delete downloaded zip which is in same folder)
|
||||
Directory.Delete(args.executableTargetDir + "/HotReload.app", true);
|
||||
} catch (IOException) {
|
||||
// ignore directory not found
|
||||
}
|
||||
Directory.CreateDirectory(args.executableTargetDir);
|
||||
UnzipMacOsPackage($"{args.executableTargetDir}/{BinaryFileName}", args.executableTargetDir + "/");
|
||||
}
|
||||
|
||||
try {
|
||||
// Always stop first because rarely it has happened that the server process was still running after custom console closed.
|
||||
// Note: this will also stop Hot Reload started by other Unity projects.
|
||||
await Stop();
|
||||
} catch {
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (args.createNoWindow) {
|
||||
await StartExecutableOrApp(args, cliExecutablePath);
|
||||
} else if (UseCustomConsoleApp()) {
|
||||
await StartExecutableOrApp(args, appExecutablePath);
|
||||
} else {
|
||||
await StartTerminal(args, cliExecutablePath);
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartExecutableOrApp(StartArgs args, string executablePath) {
|
||||
process = Process.Start(new ProcessStartInfo {
|
||||
// Path to the HotReload.app
|
||||
FileName = executablePath,
|
||||
Arguments = args.cliArguments,
|
||||
UseShellExecute = false,
|
||||
});
|
||||
|
||||
var pidFilePath = CliUtils.GetPidFilePath(args.hotreloadTempDir);
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
File.WriteAllText(pidFilePath, process.Id.ToString());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StartTerminal(StartArgs args, string executablePath) {
|
||||
var pidFilePath = CliUtils.GetPidFilePath(args.hotreloadTempDir);
|
||||
// To run in a Terminal window (so you can see compiler logs), we must put the arguments into a script file
|
||||
// and run the script in Terminal. Terminal.app does not forward the arguments passed to it via `open --args`.
|
||||
// *.command files are opened with the user's default terminal app.
|
||||
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");
|
||||
|
||||
CliUtils.Chmod(executableScriptPath); // make it executable
|
||||
CliUtils.Chmod(executablePath); // make it executable
|
||||
|
||||
Directory.CreateDirectory(args.hotreloadTempDir);
|
||||
Directory.CreateDirectory(args.executableTargetDir);
|
||||
Directory.CreateDirectory(args.cliTempDir);
|
||||
|
||||
process = Process.Start(new ProcessStartInfo {
|
||||
FileName = "open",
|
||||
Arguments = $"{(args.createNoWindow ? "-gj" : "")} '{executableScriptPath}'",
|
||||
UseShellExecute = true,
|
||||
});
|
||||
|
||||
if (process.WaitForExit(1000)) {
|
||||
if (process.ExitCode != 0) {
|
||||
Log.Warning(Translations.Errors.WarningFailedToRunServerCommand, process.ExitCode, executableScriptPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
process.EnableRaisingEvents = true;
|
||||
process.Exited += (_, __) => {
|
||||
if (process.ExitCode != 0) {
|
||||
Log.Warning(Translations.Errors.WarningFailedToRunServerCommand, process.ExitCode, executableScriptPath);
|
||||
}
|
||||
};
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Stop() {
|
||||
// kill HotReload server process (on mac it has different pid to the window which started it)
|
||||
await RequestHelper.KillServer();
|
||||
|
||||
// process.CloseMainWindow throws if proc already exited.
|
||||
// We rely on the pid file for killing the trampoline script (in-case script is just starting and HotReload server not running yet)
|
||||
process = null;
|
||||
CliUtils.KillLastKnownHotReloadProcess();
|
||||
}
|
||||
|
||||
static void UnzipMacOsPackage(string zipPath, string unzippedFolderPath) {
|
||||
//Log.Info("UnzipMacOsPackage called with {0}\n workingDirectory = {1}", zipPath, unzippedFolderPath);
|
||||
if (!zipPath.EndsWith(".zip")) {
|
||||
throw new ArgumentException(string.Format(Translations.Errors.ExceptionExpectedZipFile, zipPath), nameof(zipPath));
|
||||
}
|
||||
|
||||
if (!File.Exists(zipPath)) {
|
||||
throw new ArgumentException(string.Format(Translations.Errors.ExceptionZipFileNotFound, zipPath), nameof(zipPath));
|
||||
}
|
||||
var processStartInfo = new ProcessStartInfo {
|
||||
FileName = "unzip",
|
||||
Arguments = $"-o \"{zipPath}\"",
|
||||
WorkingDirectory = unzippedFolderPath, // unzip extracts to working directory by default
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
Process process = Process.Start(processStartInfo);
|
||||
process.WaitForExit();
|
||||
if (process.ExitCode != 0) {
|
||||
throw new Exception(string.Format(Translations.Errors.ExceptionUnzipFailed, process.ExitCode));
|
||||
}
|
||||
//Log.Info($"did unzip to {unzippedFolderPath}");
|
||||
// Move the .app folder to unzippedFolderPath
|
||||
|
||||
// find the .app directory which is now inside unzippedFolderPath directory
|
||||
var foundDirs = Directory.GetDirectories(unzippedFolderPath, "*.app", SearchOption.AllDirectories);
|
||||
var done = false;
|
||||
var destDir = unzippedFolderPath + "HotReload.app";
|
||||
foreach (var dir in foundDirs) {
|
||||
if (dir.EndsWith(".app")) {
|
||||
done = true;
|
||||
if (dir == destDir) {
|
||||
// already in the right place
|
||||
break;
|
||||
}
|
||||
Directory.Move(dir, destDir);
|
||||
//Log.Info("Moved to " + destDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
throw new Exception(string.Format(Translations.Errors.ExceptionFailedToFindAppDirectory, destDir));
|
||||
}
|
||||
//Log.Info($"did unzip to {unzippedFolderPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ebeed1c29454bc78e5a9ee64f2c9def
|
||||
timeCreated: 1673821666
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
class StartArgs {
|
||||
public string hotreloadTempDir;
|
||||
// aka method patch temp dir
|
||||
public string cliTempDir;
|
||||
public string executableTargetDir;
|
||||
public string executableSourceDir;
|
||||
public string cliArguments;
|
||||
public string unityProjDir;
|
||||
public bool createNoWindow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43d69eb7ae8aef4428da83562105bfaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Cli {
|
||||
class WindowsCliController : ICliController {
|
||||
Process process;
|
||||
|
||||
public string BinaryFileName => "CodePatcherCLI.exe";
|
||||
public string PlatformName => "win-x64";
|
||||
public bool CanOpenInBackground => true;
|
||||
|
||||
public Task Start(StartArgs args) {
|
||||
process = Process.Start(new ProcessStartInfo {
|
||||
FileName = Path.GetFullPath(Path.Combine(args.executableTargetDir, "CodePatcherCLI.exe")),
|
||||
Arguments = args.cliArguments,
|
||||
UseShellExecute = !args.createNoWindow,
|
||||
CreateNoWindow = args.createNoWindow,
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Stop() {
|
||||
await RequestHelper.KillServer();
|
||||
try {
|
||||
process?.CloseMainWindow();
|
||||
} catch {
|
||||
//ignored
|
||||
}
|
||||
process = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5644af69ec7404a8039ff2833610d48
|
||||
timeCreated: 1673822169
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80c2056f805745542a2c295385b25479
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
class DefaultCompileChecker : ICompileChecker {
|
||||
static string recompileFilePath = PackageConst.LibraryCachePath + "/recompile.txt";
|
||||
public bool hasCompileErrors { get; private set; }
|
||||
bool recompile;
|
||||
public DefaultCompileChecker() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
CompilationPipeline.assemblyCompilationFinished += DetectCompileErrors;
|
||||
CompilationPipeline.compilationFinished += OnCompilationFinished;
|
||||
var currentSessionId = EditorAnalyticsSessionInfo.id;
|
||||
Task.Run(() => {
|
||||
try {
|
||||
var compileSessionId = File.ReadAllText(recompileFilePath);
|
||||
if(compileSessionId == currentSessionId.ToString()) {
|
||||
ThreadUtility.RunOnMainThread(() => {
|
||||
recompile = true;
|
||||
_onCompilationFinished?.Invoke();
|
||||
});
|
||||
}
|
||||
File.Delete(recompileFilePath);
|
||||
} catch(DirectoryNotFoundException) {
|
||||
//dir doesn't exist -> no recompile required
|
||||
} catch(FileNotFoundException) {
|
||||
//file doesn't exist -> no recompile required
|
||||
} catch(Exception ex) {
|
||||
Log.Warning(Translations.Errors.WarningCompileCheckerIssue, ex.GetType().Name, ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DetectCompileErrors(string _, CompilerMessage[] messages) {
|
||||
for (int i = 0; i < messages.Length; i++) {
|
||||
if (messages[i].type == CompilerMessageType.Error) {
|
||||
hasCompileErrors = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
hasCompileErrors = false;
|
||||
}
|
||||
|
||||
void OnCompilationFinished(object _) {
|
||||
//Don't recompile on compile errors
|
||||
if(!hasCompileErrors) {
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(recompileFilePath));
|
||||
File.WriteAllText(recompileFilePath, EditorAnalyticsSessionInfo.id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
Action _onCompilationFinished;
|
||||
public event Action onCompilationFinished {
|
||||
add {
|
||||
if(recompile && value != null) {
|
||||
value();
|
||||
}
|
||||
_onCompilationFinished += value;
|
||||
}
|
||||
remove {
|
||||
_onCompilationFinished -= value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab09f7c657e6ecb44b65dd9f8cfc3d9f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
interface ICompileChecker {
|
||||
event Action onCompilationFinished;
|
||||
bool hasCompileErrors { get; }
|
||||
}
|
||||
|
||||
static class CompileChecker {
|
||||
internal static ICompileChecker Create() {
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
return new DefaultCompileChecker();
|
||||
#else
|
||||
return new LegacyCompileChecker();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82bf36f2126bbd1498d4964272426e0f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,55 @@
|
||||
#if !UNITY_2019_1_OR_NEWER
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
class LegacyCompileChecker : ICompileChecker {
|
||||
static readonly string timestampFilePath = PackageConst.LibraryCachePath + "/lastCompileTimestamp.txt";
|
||||
public bool hasCompileErrors { get; }
|
||||
const string assemblyPath = "Library/ScriptAssemblies";
|
||||
bool recompile;
|
||||
public LegacyCompileChecker() {
|
||||
Task.Run(() => {
|
||||
var info = new DirectoryInfo(assemblyPath);
|
||||
if(!info.Exists) {
|
||||
return;
|
||||
}
|
||||
var currentCompileTimestamp = default(DateTime);
|
||||
foreach (var file in info.GetFiles("*.dll")) {
|
||||
var fileWriteDate = file.LastWriteTimeUtc;
|
||||
if(fileWriteDate > currentCompileTimestamp) {
|
||||
currentCompileTimestamp = fileWriteDate;
|
||||
}
|
||||
}
|
||||
if(File.Exists(timestampFilePath)) {
|
||||
var lastTimestampStr = File.ReadAllText(timestampFilePath);
|
||||
var lastTimestamp = DateTime.ParseExact(lastTimestampStr, "o", CultureInfo.CurrentCulture).ToUniversalTime();
|
||||
if(currentCompileTimestamp > lastTimestamp) {
|
||||
ThreadUtility.RunOnMainThread(() => {
|
||||
recompile = true;
|
||||
_onCompilationFinished?.Invoke();
|
||||
});
|
||||
}
|
||||
}
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(timestampFilePath));
|
||||
File.WriteAllText(timestampFilePath, currentCompileTimestamp.ToString("o"));
|
||||
});
|
||||
}
|
||||
|
||||
Action _onCompilationFinished;
|
||||
public event Action onCompilationFinished {
|
||||
add {
|
||||
if(recompile && value != null) {
|
||||
value();
|
||||
}
|
||||
_onCompilationFinished += value;
|
||||
}
|
||||
remove {
|
||||
_onCompilationFinished -= value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f56ec68ce4b1fcc4b9c8ba5962d890f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,48 @@
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class Constants {
|
||||
public const string WebsiteURL = PackageConst.DefaultLocale == Locale.SimplifiedChinese ?
|
||||
"https://hotreload.net/zh" :
|
||||
"https://hotreload.net";
|
||||
public const string ProductPurchaseURL = WebsiteURL + "/pricing";
|
||||
public const string ProductPurchaseBusinessURL = ProductPurchaseURL + "?tab=business";
|
||||
public const string DocumentationURL = WebsiteURL + "/documentation";
|
||||
public const string AdditionalContentURL = DocumentationURL + "/getting-started#downloading-additional-content";
|
||||
public const string DownloadUrl = WebsiteURL + "/download";
|
||||
public const string ContactURL = WebsiteURL + "/contact";
|
||||
public const string ForumURL = "https://forum.unity.com/threads/hot-reload-edit-code-without-compiling.1389969/";
|
||||
public const string ManageLicenseURL = "https://billing.stripe.com/p/login/28odTObUQ0CU0Za3cc";
|
||||
public const string ManageAccountURL = "https://users.licensespring.com/login";
|
||||
public const string ForgotPasswordURL = "https://users.licensespring.com/reset-password";
|
||||
public const string ReportIssueURL = "https://gitlab.com/singularitygroup/hot-reload-for-unity/-/issues/new";
|
||||
public const string TroubleshootingURL = DocumentationURL + "/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 DebuggerURL = DocumentationURL + "/debugger";
|
||||
public const string UndetectedChangesURL = DocumentationURL + "/getting-started#undetected-changes";
|
||||
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";
|
||||
public const string DiscordInviteUrl = "https://discord.com/invite/kgxAS4Bqxr";
|
||||
|
||||
public const int DaysToRateApp = 5;
|
||||
public const int RecompileButtonTextHideWidth = 460;
|
||||
public const int IndicationTextHideWidth = 360;
|
||||
public const int StartButtonTextHideWidth = 400;
|
||||
public const int EventsListHideHeight = 360;
|
||||
public const int EventsListHideWidth = 425;
|
||||
public const int UpgradeLicenseNoteHideWidth = 325;
|
||||
public const int UpgradeLicenseNoteHideHeight = 150;
|
||||
public const int RateAppHideHeight = 325;
|
||||
public const int RateAppHideWidth = 300;
|
||||
public const int EventFiltersShownHideWidth = 275;
|
||||
public const int ConsumptionsHideWidth = 300;
|
||||
public const int ConsumptionsHideHeight = 360;
|
||||
|
||||
public static string Only40EntriesShown => Translations.Timeline.MessageOnly40EntriesShown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce502822e7fa34844bcb385f44091eb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Constants.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c5c2596a7a469c42a1a6b35017d8a49
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using SingularityGroup.HotReload.Demo;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Demo {
|
||||
class EditorDemo : IDemo {
|
||||
public bool IsServerRunning() {
|
||||
return ServerHealthCheck.I.IsServerHealthy;
|
||||
}
|
||||
|
||||
public void OpenHotReloadWindow() {
|
||||
HotReloadWindow.Open();
|
||||
}
|
||||
|
||||
public void OpenScriptFile(TextAsset textAsset, int line, int column) {
|
||||
var path = Path.GetFullPath(AssetDatabase.GetAssetPath(textAsset));
|
||||
#if UNITY_2019_4_OR_NEWER
|
||||
Unity.CodeEditor.CodeEditor.CurrentEditor.OpenProject(path, line, column);
|
||||
#else
|
||||
EditorUtility.OpenWithDefaultApp(path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fde6b5b57a3aeba4888a7bdaa16b3074
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs
|
||||
uploadId: 870414
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac7b192276a4a9d4f9098377d317cb2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class EditorIndicationState {
|
||||
internal enum IndicationStatus {
|
||||
Stopped,
|
||||
Started,
|
||||
Stopping,
|
||||
Installing,
|
||||
Starting,
|
||||
Reloaded,
|
||||
PartiallySupported,
|
||||
Unsupported,
|
||||
Patching,
|
||||
Loading,
|
||||
Compiling,
|
||||
CompileErrors,
|
||||
ActivationFailed,
|
||||
FinishRegistration,
|
||||
DisabledDuringDebugging,
|
||||
Undetected,
|
||||
Paused,
|
||||
}
|
||||
|
||||
internal static readonly string greyIconPath = "grey";
|
||||
internal static readonly string greenIconPath = "green";
|
||||
internal static readonly string redIconPath = "red";
|
||||
private static readonly Dictionary<IndicationStatus, string> IndicationIcon = new Dictionary<IndicationStatus, string> {
|
||||
// grey icon:
|
||||
{ IndicationStatus.FinishRegistration, greyIconPath },
|
||||
{ IndicationStatus.Stopped, greyIconPath },
|
||||
{ IndicationStatus.Paused, greyIconPath },
|
||||
// green icon:
|
||||
{ IndicationStatus.Started, greenIconPath },
|
||||
// log icons:
|
||||
{ IndicationStatus.Reloaded, HotReloadTimelineHelper.alertIconString[AlertType.AppliedChange] },
|
||||
{ IndicationStatus.Unsupported, HotReloadTimelineHelper.alertIconString[AlertType.UnsupportedChange] },
|
||||
{ IndicationStatus.Undetected, HotReloadTimelineHelper.alertIconString[AlertType.UndetectedChange] },
|
||||
{ IndicationStatus.DisabledDuringDebugging, HotReloadTimelineHelper.alertIconString[AlertType.UnsupportedChange] },
|
||||
{ IndicationStatus.PartiallySupported, HotReloadTimelineHelper.alertIconString[AlertType.PartiallySupportedChange] },
|
||||
{ IndicationStatus.CompileErrors, HotReloadTimelineHelper.alertIconString[AlertType.CompileError] },
|
||||
// spinner:
|
||||
{ IndicationStatus.Stopping, Spinner.SpinnerIconPath },
|
||||
{ IndicationStatus.Starting, Spinner.SpinnerIconPath },
|
||||
{ IndicationStatus.Patching, Spinner.SpinnerIconPath },
|
||||
{ IndicationStatus.Loading, Spinner.SpinnerIconPath },
|
||||
{ IndicationStatus.Compiling, Spinner.SpinnerIconPath },
|
||||
{ IndicationStatus.Installing, Spinner.SpinnerIconPath },
|
||||
// red icon:
|
||||
{ IndicationStatus.ActivationFailed, redIconPath },
|
||||
};
|
||||
|
||||
private static readonly IndicationStatus[] SpinnerIndications = IndicationIcon
|
||||
.Where(kvp => kvp.Value == Spinner.SpinnerIconPath)
|
||||
.Select(kvp => kvp.Key)
|
||||
.ToArray();
|
||||
|
||||
// NOTE: if you add longer text, make sure UI is wide enough for it
|
||||
public static Dictionary<IndicationStatus, string> IndicationText => new Dictionary<IndicationStatus, string> {
|
||||
{ IndicationStatus.FinishRegistration, Translations.Miscellaneous.IndicationFinishRegistration },
|
||||
{ IndicationStatus.Started, Translations.Miscellaneous.IndicationStarted },
|
||||
{ IndicationStatus.Stopping, Translations.Miscellaneous.IndicationStopping },
|
||||
{ IndicationStatus.Stopped, Translations.Miscellaneous.IndicationStopped },
|
||||
{ IndicationStatus.Paused, Translations.Miscellaneous.IndicationPaused },
|
||||
{ IndicationStatus.Installing, Translations.Miscellaneous.IndicationInstalling },
|
||||
{ IndicationStatus.Starting, Translations.Miscellaneous.IndicationStarting },
|
||||
{ IndicationStatus.Reloaded, Translations.Miscellaneous.IndicationReloaded },
|
||||
{ IndicationStatus.PartiallySupported, Translations.Miscellaneous.IndicationPartiallySupported },
|
||||
{ IndicationStatus.Unsupported, Translations.Miscellaneous.IndicationUnsupported },
|
||||
{ IndicationStatus.Patching, Translations.Miscellaneous.IndicationPatching },
|
||||
{ IndicationStatus.DisabledDuringDebugging, Translations.Miscellaneous.IndicationDisabledDuringDebugging },
|
||||
{ IndicationStatus.Compiling, Translations.Miscellaneous.IndicationCompiling },
|
||||
{ IndicationStatus.CompileErrors, Translations.Miscellaneous.IndicationCompileErrors },
|
||||
{ IndicationStatus.ActivationFailed, Translations.Miscellaneous.IndicationActivationFailed },
|
||||
{ IndicationStatus.Loading, Translations.Miscellaneous.IndicationLoading },
|
||||
{ IndicationStatus.Undetected, Translations.Miscellaneous.IndicationUndetected},
|
||||
};
|
||||
|
||||
private const int MinSpinnerDuration = 200;
|
||||
private static DateTime spinnerStartedAt;
|
||||
private static IndicationStatus latestStatus;
|
||||
private static bool SpinnerCompletedMinDuration => DateTime.UtcNow - spinnerStartedAt > TimeSpan.FromMilliseconds(MinSpinnerDuration);
|
||||
private static IndicationStatus GetIndicationStatus() {
|
||||
var status = GetIndicationStatusCore();
|
||||
|
||||
// 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) {
|
||||
if (newStatusIsSpinner) {
|
||||
return status;
|
||||
} else if (SpinnerCompletedMinDuration) {
|
||||
latestStatus = status;
|
||||
return status;
|
||||
} else {
|
||||
return latestStatus;
|
||||
}
|
||||
} else if (newStatusIsSpinner) {
|
||||
spinnerStartedAt = DateTime.UtcNow;
|
||||
latestStatus = status;
|
||||
return status;
|
||||
} else {
|
||||
spinnerStartedAt = DateTime.UtcNow;
|
||||
latestStatus = IndicationStatus.Loading;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
private static IndicationStatus GetIndicationStatusCore() {
|
||||
if (RedeemLicenseHelper.I.RegistrationRequired)
|
||||
return IndicationStatus.FinishRegistration;
|
||||
if (EditorCodePatcher.DownloadRequired && EditorCodePatcher.DownloadStarted || EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Starting && !EditorCodePatcher.Stopping)
|
||||
return IndicationStatus.Installing;
|
||||
if (EditorCodePatcher.Stopping)
|
||||
return IndicationStatus.Stopping;
|
||||
if (EditorCodePatcher.Compiling && !EditorCodePatcher.Stopping && !EditorCodePatcher.Starting && EditorCodePatcher.Running)
|
||||
return IndicationStatus.Compiling;
|
||||
if (EditorCodePatcher.Starting && !EditorCodePatcher.Stopping)
|
||||
return IndicationStatus.Starting;
|
||||
if (!Application.isPlaying && HotReloadPrefs.PauseHotReloadInEditMode)
|
||||
return IndicationStatus.Paused;
|
||||
if (!EditorCodePatcher.Running)
|
||||
return IndicationStatus.Stopped;
|
||||
if (EditorCodePatcher.Status?.isLicensed != true && EditorCodePatcher.Status?.isFree != true && EditorCodePatcher.Status?.freeSessionFinished == true)
|
||||
return IndicationStatus.ActivationFailed;
|
||||
if (EditorCodePatcher.compileError)
|
||||
return IndicationStatus.CompileErrors;
|
||||
|
||||
// fallback on patch status
|
||||
if (!EditorCodePatcher.Started && !EditorCodePatcher.Running) {
|
||||
return IndicationStatus.Stopped;
|
||||
}
|
||||
if (Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled) {
|
||||
return IndicationStatus.DisabledDuringDebugging;
|
||||
}
|
||||
switch (EditorCodePatcher.patchStatus) {
|
||||
case PatchStatus.Idle:
|
||||
if (!EditorCodePatcher.Compiling && !EditorCodePatcher.firstPatchAttempted && !EditorCodePatcher.compileError) {
|
||||
return IndicationStatus.Started;
|
||||
}
|
||||
if (EditorCodePatcher._applyingFailed) {
|
||||
return IndicationStatus.Unsupported;
|
||||
}
|
||||
if (EditorCodePatcher._appliedPartially) {
|
||||
return IndicationStatus.PartiallySupported;
|
||||
}
|
||||
if (EditorCodePatcher._appliedUndetected) {
|
||||
return IndicationStatus.Undetected;
|
||||
}
|
||||
return IndicationStatus.Reloaded;
|
||||
case PatchStatus.Patching: return IndicationStatus.Patching;
|
||||
case PatchStatus.Unsupported: return IndicationStatus.Unsupported;
|
||||
case PatchStatus.Compiling: return IndicationStatus.Compiling;
|
||||
case PatchStatus.CompileError: return IndicationStatus.CompileErrors;
|
||||
case PatchStatus.None:
|
||||
default: return IndicationStatus.Reloaded;
|
||||
}
|
||||
}
|
||||
|
||||
internal static IndicationStatus CurrentIndicationStatus => GetIndicationStatus();
|
||||
internal static bool SpinnerActive => SpinnerIndications.Contains(CurrentIndicationStatus);
|
||||
internal static string IndicationIconPath => IndicationIcon[CurrentIndicationStatus];
|
||||
internal static string IndicationStatusText {
|
||||
get {
|
||||
var indicationStatus = CurrentIndicationStatus;
|
||||
string txt;
|
||||
if (indicationStatus == IndicationStatus.Starting && EditorCodePatcher.StartupProgress != null) {
|
||||
txt = EditorCodePatcher.StartupProgress.Item2;
|
||||
} else if (!IndicationText.TryGetValue(indicationStatus, out txt)) {
|
||||
Log.Warning(Translations.Errors.WarningIndicationTextNotFound, indicationStatus);
|
||||
} else {
|
||||
txt = IndicationText[indicationStatus];
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee342ddb17e444c7a8927be3bd792ae2
|
||||
timeCreated: 1686087206
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class GitUtil {
|
||||
/// <remarks>
|
||||
/// Fallback is PatchServerInfo.UnknownCommitHash
|
||||
/// </remarks>
|
||||
public static string GetShortCommitHashOrFallback(int timeoutAfterMillis = 5000) {
|
||||
var shortCommitHash = PatchServerInfo.UnknownCommitHash;
|
||||
|
||||
var commitHash = GetShortCommitHashSafe(timeoutAfterMillis);
|
||||
// On MacOS GetShortCommitHash() returns 7 characters, on Windows it returns 8 characters.
|
||||
// When git command produced an unexpected result, use a fallback string
|
||||
if (commitHash != null && commitHash.Length >= 6) {
|
||||
shortCommitHash = commitHash.Length < 8 ? commitHash : commitHash.Substring(0, 8);
|
||||
}
|
||||
|
||||
return shortCommitHash;
|
||||
}
|
||||
|
||||
// only log exception once per domain reload, to prevent spamming the console
|
||||
private static bool loggedExceptionInGetShortCommitHashSafe = false;
|
||||
|
||||
/// <summary>
|
||||
/// Get the git commit hash, returning null if it takes too long.
|
||||
/// </summary>
|
||||
/// <param name="timeoutAfterMillis"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This method is 'better safe than sorry' because we must not break the user's build.<br/>
|
||||
/// It is better to not know the commit hash than to fail the build.
|
||||
/// </remarks>
|
||||
private static string GetShortCommitHashSafe(int timeoutAfterMillis) {
|
||||
Process process = null;
|
||||
// Note: don't use ReadToEndAsync because waiting on that task blocks forever.
|
||||
try {
|
||||
process = StartGitCommand("log", " -n 1 --pretty=format:%h");
|
||||
var stdout = process.StandardOutput;
|
||||
if (process.WaitForExit(timeoutAfterMillis)) {
|
||||
return stdout.ReadToEnd();
|
||||
} else {
|
||||
// In a git repo with git lfs, git log can be blocked by waiting for switch branches / download lfs objects
|
||||
// For that reason I disabled this warning log until a better solution is implemented (e.g. cache the commit and use cached if timeout).
|
||||
// Log.Warning(
|
||||
// $"[{CodePatcher.TAG}] Timed out trying to get the git commit hash, HotReload will not warn you about" +
|
||||
// " a build connecting to a server running on a different commit (which is not supported)");
|
||||
return null;
|
||||
}
|
||||
} catch (Win32Exception ex) {
|
||||
if (ex.NativeErrorCode == 2) {
|
||||
// git not found, ignore because user doesn't use git for version control
|
||||
return null;
|
||||
} else if (!loggedExceptionInGetShortCommitHashSafe) {
|
||||
loggedExceptionInGetShortCommitHashSafe = true;
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
if (!loggedExceptionInGetShortCommitHashSafe) {
|
||||
loggedExceptionInGetShortCommitHashSafe = true;
|
||||
Log.Exception(ex);
|
||||
}
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.Dispose();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Process StartGitCommand(string command, string arguments, Action<ProcessStartInfo> modifySettings = null) {
|
||||
var startInfo = new ProcessStartInfo("git", command + " " + arguments) {
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
};
|
||||
if (modifySettings != null) {
|
||||
modifySettings(startInfo);
|
||||
}
|
||||
return Process.Start(startInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f994bd5bb9f33f740ae37f8c79048a10
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 387b31d7da35b27428629a83bb4ac589
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEditor.Compilation;
|
||||
|
||||
[assembly: InternalsVisibleTo("SingularityGroup.HotReload.EditorTests")]
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class AssemblyOmission {
|
||||
// [MenuItem("Window/Hot Reload Dev/List omitted projects")]
|
||||
private static void Check() {
|
||||
Log.Info(Translations.Errors.InfoOmitProjectsForPlayerBuild);
|
||||
var omitted = GetOmittedProjects(EditorUserBuildSettings.activeScriptCompilationDefines);
|
||||
Log.Info(Translations.Errors.InfoSeparator);
|
||||
|
||||
foreach (var name in omitted) {
|
||||
Log.Info(Translations.Errors.InfoOmittedEditorProject, name);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonObject(MemberSerialization.Fields)]
|
||||
private class AssemblyDefinitionJson {
|
||||
public string name;
|
||||
public string[] defineConstraints;
|
||||
}
|
||||
|
||||
// scripts in Assets/ (with no asmdef) are always compiled into Assembly-CSharp
|
||||
private static readonly string alwaysIncluded = "Assembly-CSharp";
|
||||
|
||||
private class Cache : AssetPostprocessor {
|
||||
public static string[] ommitedProjects;
|
||||
|
||||
private static void OnPostprocessAllAssets(string[] importedAssets,
|
||||
string[] deletedAssets,
|
||||
string[] movedAssets,
|
||||
string[] movedFromAssetPaths) {
|
||||
ommitedProjects = null;
|
||||
}
|
||||
}
|
||||
|
||||
// main thread only
|
||||
public static string[] GetOmittedProjects(string allDefineSymbols, bool verboseLogs = false) {
|
||||
if (Cache.ommitedProjects != null) {
|
||||
return Cache.ommitedProjects;
|
||||
}
|
||||
var arr = allDefineSymbols.Split(';');
|
||||
var omitted = GetOmittedProjects(arr, verboseLogs);
|
||||
Cache.ommitedProjects = omitted;
|
||||
return omitted;
|
||||
}
|
||||
|
||||
// must be deterministic (return projects in same order each time)
|
||||
private static string[] GetOmittedProjects(string[] allDefineSymbols, bool verboseLogs = false) {
|
||||
// HotReload uses names of assemblies.
|
||||
var editorAssemblies = GetEditorAssemblies();
|
||||
|
||||
editorAssemblies.Remove(alwaysIncluded);
|
||||
var omittedByConstraint = DefineConstraints.GetOmittedAssemblies(allDefineSymbols);
|
||||
editorAssemblies.AddRange(omittedByConstraint);
|
||||
|
||||
// Note: other platform player assemblies are also returned here, but I haven't seen it cause issues
|
||||
// when using Hot Reload with IdleGame Android build.
|
||||
var playerAssemblies = GetPlayerAssemblies().ToArray();
|
||||
|
||||
if (verboseLogs) {
|
||||
foreach (var name in editorAssemblies) {
|
||||
Log.Info(Translations.Errors.InfoFoundProjectNamed, name);
|
||||
}
|
||||
foreach (var playerAssemblyName in playerAssemblies) {
|
||||
Log.Debug(string.Format(Translations.Utility.PlayerAssemblyDebug, playerAssemblyName));
|
||||
}
|
||||
}
|
||||
// leaves the editor assemblies that are not built into player assemblies (e.g. editor and test assemblies)
|
||||
var toOmit = editorAssemblies.Except(playerAssemblies.Select(asm => asm.name));
|
||||
var unique = new HashSet<string>(toOmit);
|
||||
return unique.OrderBy(s => s).ToArray();
|
||||
}
|
||||
|
||||
// main thread only
|
||||
public static List<string> GetEditorAssemblies() {
|
||||
return CompilationPipeline
|
||||
.GetAssemblies(AssembliesType.Editor)
|
||||
.Select(asm => asm.name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static Assembly[] GetPlayerAssemblies() {
|
||||
var playerAssemblyNames = CompilationPipeline
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
.GetAssemblies(AssembliesType.PlayerWithoutTestAssemblies) // since Unity 2019.3
|
||||
#else
|
||||
.GetAssemblies(AssembliesType.Player)
|
||||
#endif
|
||||
.ToArray();
|
||||
|
||||
|
||||
return playerAssemblyNames;
|
||||
}
|
||||
|
||||
internal static class DefineConstraints {
|
||||
/// <summary>
|
||||
/// When define constraints evaluate to false, we need
|
||||
/// </summary>
|
||||
/// <param name="defineSymbols"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Not aware of a Unity api to read defineConstraints, so we do it ourselves.<br/>
|
||||
/// Find any asmdef files where the define constraints evaluate to false.
|
||||
/// </remarks>
|
||||
public static string[] GetOmittedAssemblies(string[] defineSymbols) {
|
||||
var guids = AssetDatabase.FindAssets("t:asmdef");
|
||||
var asmdefFiles = guids.Select(AssetDatabase.GUIDToAssetPath);
|
||||
var shouldOmit = new List<string>();
|
||||
foreach (var asmdefFile in asmdefFiles) {
|
||||
var asmdef = ReadDefineConstraints(asmdefFile);
|
||||
if (asmdef == null) continue;
|
||||
if (asmdef.defineConstraints == null || asmdef.defineConstraints.Length == 0) {
|
||||
// Hot Reload already handles assemblies correctly if they have no define symbols.
|
||||
continue;
|
||||
}
|
||||
|
||||
var allPass = asmdef.defineConstraints.All(constraint => EvaluateDefineConstraint(constraint, defineSymbols));
|
||||
if (!allPass) {
|
||||
shouldOmit.Add(asmdef.name);
|
||||
}
|
||||
}
|
||||
|
||||
return shouldOmit.ToArray();
|
||||
}
|
||||
|
||||
static AssemblyDefinitionJson ReadDefineConstraints(string path) {
|
||||
try {
|
||||
var json = File.ReadAllText(path);
|
||||
var asmdef = JsonConvert.DeserializeObject<AssemblyDefinitionJson>(json);
|
||||
return asmdef;
|
||||
} catch (Exception) {
|
||||
// ignore malformed asmdef
|
||||
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", "||" },
|
||||
{ "AND", "&&" },
|
||||
{ "NOT", "!" }
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate a define constraint like 'UNITY_ANDROID || UNITY_IOS'
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="defineSymbols"></param>
|
||||
/// <returns></returns>
|
||||
public static bool EvaluateDefineConstraint(string input, string[] defineSymbols) {
|
||||
// map Unity defineConstraints syntax to DataTable syntax (unity supports both)
|
||||
foreach (var item in syntaxMap) {
|
||||
// surround with space because || may not have spaces around it
|
||||
input = input.Replace(item.Value, $" {item.Key} ");
|
||||
}
|
||||
|
||||
// remove any extra spaces we just created
|
||||
input = input.Replace(" ", " ");
|
||||
|
||||
var tokens = input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var token in tokens) {
|
||||
if (!syntaxMap.ContainsKey(token) && token != "false" && token != "true") {
|
||||
var index = input.IndexOf(token, StringComparison.Ordinal);
|
||||
|
||||
// replace symbols with true or false depending if they are in the array or not.
|
||||
input = input.Substring(0, index) + defineSymbols.Contains(token) + input.Substring(index + token.Length);
|
||||
}
|
||||
}
|
||||
|
||||
var dt = new DataTable();
|
||||
return (bool)dt.Compute(input, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b94f2314a044b109de488be1ccd5640
|
||||
timeCreated: 1674233674
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
struct BuildInfoInput {
|
||||
public readonly string allDefineSymbols;
|
||||
public readonly BuildTarget activeBuildTarget;
|
||||
public readonly string[] omittedProjects;
|
||||
public readonly bool batchMode;
|
||||
public readonly string locale;
|
||||
|
||||
public BuildInfoInput(string allDefineSymbols, BuildTarget activeBuildTarget, string[] omittedProjects, bool batchMode, string locale) {
|
||||
this.allDefineSymbols = allDefineSymbols;
|
||||
this.activeBuildTarget = activeBuildTarget;
|
||||
this.omittedProjects = omittedProjects;
|
||||
this.batchMode = batchMode;
|
||||
this.locale = locale;
|
||||
}
|
||||
}
|
||||
|
||||
static class BuildInfoHelper {
|
||||
public static async Task<BuildInfoInput> GetGenerateBuildInfoInput() {
|
||||
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
|
||||
var activeDefineSymbols = EditorUserBuildSettings.activeScriptCompilationDefines;
|
||||
var batchMode = Application.isBatchMode;
|
||||
var allDefineSymbols = await Task.Run(() => {
|
||||
return GetAllAndroidMonoBuildDefineSymbolsThreaded(activeDefineSymbols);
|
||||
});
|
||||
// cached so unexpensive most of the time
|
||||
var omittedProjects = AssemblyOmission.GetOmittedProjects(allDefineSymbols);
|
||||
var locale = PackageConst.DefaultLocale;
|
||||
|
||||
return new BuildInfoInput(
|
||||
allDefineSymbols: allDefineSymbols,
|
||||
activeBuildTarget: buildTarget,
|
||||
omittedProjects: omittedProjects,
|
||||
batchMode: batchMode,
|
||||
locale: locale
|
||||
);
|
||||
}
|
||||
|
||||
public static BuildInfo GenerateBuildInfoMainThread() {
|
||||
return GenerateBuildInfoMainThread(EditorUserBuildSettings.activeBuildTarget);
|
||||
}
|
||||
|
||||
public static BuildInfo GenerateBuildInfoMainThread(BuildTarget buildTarget) {
|
||||
var allDefineSymbols = GetAllAndroidMonoBuildDefineSymbolsThreaded(EditorUserBuildSettings.activeScriptCompilationDefines);
|
||||
return GenerateBuildInfoThreaded(new BuildInfoInput(
|
||||
allDefineSymbols: allDefineSymbols,
|
||||
activeBuildTarget: buildTarget,
|
||||
omittedProjects: AssemblyOmission.GetOmittedProjects(allDefineSymbols),
|
||||
batchMode: Application.isBatchMode,
|
||||
locale: PackageConst.DefaultLocale
|
||||
));
|
||||
}
|
||||
|
||||
public static BuildInfo GenerateBuildInfoThreaded(BuildInfoInput input) {
|
||||
var omittedProjectRegex = String.Join("|", input.omittedProjects.Select(name => Regex.Escape(name)));
|
||||
var shortCommitHash = GitUtil.GetShortCommitHashOrFallback();
|
||||
var hostname = IsHumanControllingUs(input.batchMode) ? IpHelper.GetIpAddress() : null;
|
||||
|
||||
// Note: add a string to uniquely identify the Unity project. Could use filepath to /MyProject/Assets/ (editor Application.dataPath)
|
||||
// or application identifier (com.company.appname).
|
||||
// Do this when supporting multiple projects: SG-28807
|
||||
// The matching code is in Runtime assembly which compares server response with built BuildInfo.
|
||||
return new BuildInfo {
|
||||
projectIdentifier = "SG-29580",
|
||||
commitHash = shortCommitHash,
|
||||
defineSymbols = input.allDefineSymbols,
|
||||
projectOmissionRegex = omittedProjectRegex,
|
||||
buildMachineHostName = hostname,
|
||||
buildMachinePort = RequestHelper.port,
|
||||
activeBuildTarget = input.activeBuildTarget.ToString(),
|
||||
buildMachineRequestOrigin = RequestHelper.origin,
|
||||
locale = input.locale
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsHumanControllingUs(bool batchMode) {
|
||||
if (batchMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"));
|
||||
return !isCI;
|
||||
}
|
||||
|
||||
private static readonly string[] editorSymbolsToRemove = {
|
||||
"PLATFORM_ARCH_64",
|
||||
"UNITY_64",
|
||||
"UNITY_INCLUDE_TESTS",
|
||||
"UNITY_EDITOR",
|
||||
"UNITY_EDITOR_64",
|
||||
"UNITY_EDITOR_WIN",
|
||||
"ENABLE_UNITY_COLLECTIONS_CHECKS",
|
||||
"ENABLE_BURST_AOT",
|
||||
"RENDER_SOFTWARE_CURSOR",
|
||||
"PLATFORM_STANDALONE_WIN",
|
||||
"PLATFORM_STANDALONE",
|
||||
"UNITY_STANDALONE_WIN",
|
||||
"UNITY_STANDALONE",
|
||||
"ENABLE_MOVIES",
|
||||
"ENABLE_OUT_OF_PROCESS_CRASH_HANDLER",
|
||||
"ENABLE_WEBSOCKET_HOST",
|
||||
"ENABLE_CLUSTER_SYNC",
|
||||
"ENABLE_CLUSTERINPUT",
|
||||
};
|
||||
|
||||
private static readonly string[] androidSymbolsToAdd = {
|
||||
"CSHARP_7_OR_LATER",
|
||||
"CSHARP_7_3_OR_NEWER",
|
||||
"PLATFORM_ANDROID",
|
||||
"UNITY_ANDROID",
|
||||
"UNITY_ANDROID_API",
|
||||
"ENABLE_EGL",
|
||||
"DEVELOPMENT_BUILD",
|
||||
"ENABLE_CLOUD_SERVICES_NATIVE_CRASH_REPORTING",
|
||||
"PLATFORM_SUPPORTS_ADS_ID",
|
||||
"UNITY_CAN_SHOW_SPLASH_SCREEN",
|
||||
"UNITY_HAS_GOOGLEVR",
|
||||
"UNITY_HAS_TANGO",
|
||||
"ENABLE_SPATIALTRACKING",
|
||||
"ENABLE_RUNTIME_PERMISSIONS",
|
||||
"ENABLE_ENGINE_CODE_STRIPPING",
|
||||
"UNITY_ASTC_ONLY_DECOMPRESS",
|
||||
"ANDROID_USE_SWAPPY",
|
||||
"ENABLE_ONSCREEN_KEYBOARD",
|
||||
"ENABLE_UNITYADS_RUNTIME",
|
||||
"UNITY_UNITYADS_API",
|
||||
};
|
||||
|
||||
// Currently there is no better way. Alternatively we could hook into unity's call to csc.exe and parse the /define: arguments.
|
||||
// Hardcoding the differences was less effort and is less error prone.
|
||||
// I also looked into it and tried all the Build interfaces like this one https://docs.unity3d.com/ScriptReference/Build.IPostBuildPlayerScriptDLLs.html
|
||||
// and logging EditorUserBuildSettings.activeScriptCompilationDefines in the callbacks - result: all same like editor, so I agree that hardcode is best.
|
||||
private static string GetAllAndroidMonoBuildDefineSymbolsThreaded(string[] defineSymbols) {
|
||||
var defines = new HashSet<string>(defineSymbols);
|
||||
defines.ExceptWith(editorSymbolsToRemove);
|
||||
defines.UnionWith(androidSymbolsToAdd);
|
||||
// sort for consistency, must be deterministic
|
||||
var definesArray = defines.OrderBy(def => def).ToArray();
|
||||
return String.Join(";", definesArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f41ad09ae4f04088bf6c9ad9a4fc0885
|
||||
timeCreated: 1674220023
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class EditorWindowHelper {
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
public static bool supportsNotifications = true;
|
||||
#else
|
||||
public static bool supportsNotifications = false;
|
||||
#endif
|
||||
|
||||
private static readonly Regex ValidEmailRegex = new Regex(@"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|"
|
||||
+ @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)"
|
||||
+ @"@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$", RegexOptions.IgnoreCase);
|
||||
|
||||
public static bool IsValidEmailAddress(string email) {
|
||||
return ValidEmailRegex.IsMatch(email);
|
||||
}
|
||||
|
||||
public static bool IsHumanControllingUs() {
|
||||
if (Application.isBatchMode) {
|
||||
// allow for running tests
|
||||
if (MultiplayerPlaymodeHelper.HasCommandLineArgument(Environment.GetCommandLineArgs(), "-runTests")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"));
|
||||
return !isCI;
|
||||
}
|
||||
|
||||
internal enum NotificationStatus {
|
||||
None,
|
||||
Patching,
|
||||
NeedsRecompile
|
||||
}
|
||||
|
||||
private static Dictionary<NotificationStatus, GUIContent> notificationContent => new Dictionary<NotificationStatus, GUIContent> {
|
||||
{ NotificationStatus.Patching, new GUIContent(Translations.Miscellaneous.NotificationPatching)},
|
||||
{ NotificationStatus.NeedsRecompile, new GUIContent(Translations.Miscellaneous.NotificationNeedsRecompile)},
|
||||
};
|
||||
|
||||
static Type gameViewT;
|
||||
private static EditorWindow[] gameViewWindows {
|
||||
get {
|
||||
gameViewT = gameViewT ?? typeof(EditorWindow).Assembly.GetType("UnityEditor.GameView");
|
||||
return Resources.FindObjectsOfTypeAll(gameViewT).Cast<EditorWindow>().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static EditorWindow[] sceneWindows {
|
||||
get {
|
||||
return Resources.FindObjectsOfTypeAll(typeof(SceneView)).Cast<EditorWindow>().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static EditorWindow[] notificationWindows {
|
||||
get {
|
||||
return gameViewWindows.Concat(sceneWindows).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
static NotificationStatus lastNotificationStatus;
|
||||
private static DateTime? latestNotificationStartedAt;
|
||||
private static bool notificationShownRecently => latestNotificationStartedAt != null && DateTime.UtcNow - latestNotificationStartedAt < TimeSpan.FromSeconds(1);
|
||||
internal static void ShowNotification(NotificationStatus notificationType, float maxDuration = 3) {
|
||||
// Patch status goes from Unsupported changes to patching rapidly when making unsupported change
|
||||
// patching also shows right before unsupported changes sometimes
|
||||
// so we don't override NeedsRecompile notification ever
|
||||
bool willOverrideNeedsCompileNotification = notificationType != NotificationStatus.NeedsRecompile && notificationShownRecently || lastNotificationStatus == NotificationStatus.NeedsRecompile && notificationShownRecently;
|
||||
if (!supportsNotifications || willOverrideNeedsCompileNotification) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (EditorWindow notificationWindow in notificationWindows) {
|
||||
notificationWindow.ShowNotification(notificationContent[notificationType], maxDuration);
|
||||
notificationWindow.Repaint();
|
||||
}
|
||||
latestNotificationStartedAt = DateTime.UtcNow;
|
||||
lastNotificationStatus = notificationType;
|
||||
}
|
||||
|
||||
internal static void RemoveNotification() {
|
||||
if (!supportsNotifications) {
|
||||
return;
|
||||
}
|
||||
// only patching notifications should be removed after showing less than 1 second
|
||||
if (notificationShownRecently && lastNotificationStatus != NotificationStatus.Patching) {
|
||||
return;
|
||||
}
|
||||
foreach (EditorWindow notificationWindow in notificationWindows) {
|
||||
notificationWindow.RemoveNotification();
|
||||
notificationWindow.Repaint();
|
||||
}
|
||||
latestNotificationStartedAt = null;
|
||||
lastNotificationStatus = NotificationStatus.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd463b1f0bfddf34caa662ebe375e5fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,162 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal enum InvertibleIcon {
|
||||
BugReport,
|
||||
Events,
|
||||
EventsNew,
|
||||
Recompile,
|
||||
Logo,
|
||||
Close,
|
||||
FoldoutOpen,
|
||||
FoldoutClosed,
|
||||
Spinner,
|
||||
Stop,
|
||||
Start,
|
||||
}
|
||||
|
||||
internal static class GUIHelper {
|
||||
private static readonly Dictionary<InvertibleIcon, string> supportedInvertibleIcons = new Dictionary<InvertibleIcon, string> {
|
||||
{ InvertibleIcon.BugReport, "report_bug" },
|
||||
{ InvertibleIcon.Events, "events" },
|
||||
{ InvertibleIcon.Recompile, "refresh" },
|
||||
{ InvertibleIcon.Logo, "logo" },
|
||||
{ InvertibleIcon.Close, "close" },
|
||||
{ InvertibleIcon.FoldoutOpen, "foldout_open" },
|
||||
{ InvertibleIcon.FoldoutClosed, "foldout_closed" },
|
||||
{ InvertibleIcon.Spinner, "icon_loading_star_light_mode_96" },
|
||||
{ InvertibleIcon.Stop, "Icn_Stop" },
|
||||
{ InvertibleIcon.Start, "Icn_play" },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<InvertibleIcon, Texture2D> invertibleIconCache = new Dictionary<InvertibleIcon, Texture2D>();
|
||||
private static readonly Dictionary<InvertibleIcon, Texture2D> invertibleIconInvertedCache = new Dictionary<InvertibleIcon, Texture2D>();
|
||||
private static readonly Dictionary<string, Texture2D> iconCache = new Dictionary<string, Texture2D>();
|
||||
|
||||
internal static Texture2D InvertTextureColor(Texture2D originalTexture) {
|
||||
if (!originalTexture) {
|
||||
return originalTexture;
|
||||
}
|
||||
// Get the original pixels from the texture
|
||||
Color[] originalPixels = originalTexture.GetPixels();
|
||||
|
||||
// Create a new array for the inverted colors
|
||||
Color[] invertedPixels = new Color[originalPixels.Length];
|
||||
|
||||
// Iterate through the pixels and invert the colors while preserving the alpha channel
|
||||
for (int i = 0; i < originalPixels.Length; i++) {
|
||||
Color originalColor = originalPixels[i];
|
||||
Color invertedColor = new Color(1 - originalColor.r, 1 - originalColor.g, 1 - originalColor.b, originalColor.a);
|
||||
invertedPixels[i] = invertedColor;
|
||||
}
|
||||
|
||||
// Create a new texture and set its pixels
|
||||
Texture2D invertedTexture = new Texture2D(originalTexture.width, originalTexture.height);
|
||||
invertedTexture.SetPixels(invertedPixels);
|
||||
|
||||
// Apply the changes to the texture
|
||||
invertedTexture.Apply();
|
||||
|
||||
return invertedTexture;
|
||||
}
|
||||
|
||||
internal static Texture2D GetInvertibleIcon(InvertibleIcon invertibleIcon) {
|
||||
Texture2D iconTexture;
|
||||
var cache = HotReloadWindowStyles.IsDarkMode ? invertibleIconInvertedCache : invertibleIconCache;
|
||||
|
||||
if (!cache.TryGetValue(invertibleIcon, out iconTexture) || !iconTexture) {
|
||||
var type = invertibleIcon == InvertibleIcon.EventsNew ? InvertibleIcon.Events : invertibleIcon;
|
||||
iconTexture = Resources.Load<Texture2D>(supportedInvertibleIcons[type]);
|
||||
|
||||
// we assume icons are for light mode by default
|
||||
// therefore if its dark mode we should invert them
|
||||
if (HotReloadWindowStyles.IsDarkMode) {
|
||||
iconTexture = InvertTextureColor(iconTexture);
|
||||
}
|
||||
|
||||
cache[type] = iconTexture;
|
||||
|
||||
// we combine dot image with Events icon to create a new alert version
|
||||
if (invertibleIcon == InvertibleIcon.EventsNew) {
|
||||
var redDot = Resources.Load<Texture2D>("red_dot");
|
||||
iconTexture = CombineImages(iconTexture, redDot);
|
||||
cache[InvertibleIcon.EventsNew] = iconTexture;
|
||||
}
|
||||
}
|
||||
return cache[invertibleIcon];
|
||||
}
|
||||
|
||||
internal static Texture2D GetLocalIcon(string iconName) {
|
||||
Texture2D iconTexture;
|
||||
if (!iconCache.TryGetValue(iconName, out iconTexture) || !iconTexture) {
|
||||
iconTexture = Resources.Load<Texture2D>(iconName);
|
||||
iconCache[iconName] = iconTexture;
|
||||
}
|
||||
return iconTexture;
|
||||
}
|
||||
|
||||
static Texture2D CombineImages(Texture2D image1, Texture2D image2) {
|
||||
if (!image1 || !image2) {
|
||||
return image1;
|
||||
}
|
||||
var combinedImage = new Texture2D(Mathf.Max(image1.width, image2.width), Mathf.Max(image1.height, image2.height));
|
||||
|
||||
for (int y = 0; y < combinedImage.height; y++) {
|
||||
for (int x = 0; x < combinedImage.width; x++) {
|
||||
Color color1 = x < image1.width && y < image1.height ? image1.GetPixel(x, y) : Color.clear;
|
||||
Color color2 = x < image2.width && y < image2.height ? image2.GetPixel(x, y) : Color.clear;
|
||||
combinedImage.SetPixel(x, y, Color.Lerp(color1, color2, color2.a));
|
||||
}
|
||||
}
|
||||
combinedImage.Apply();
|
||||
return combinedImage;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Color, Texture2D> textureColorCache = new Dictionary<Color, Texture2D>();
|
||||
internal static Texture2D ConvertTextureToColor(Color color) {
|
||||
Texture2D texture;
|
||||
if (!textureColorCache.TryGetValue(color, out texture) || !texture) {
|
||||
texture = new Texture2D(1, 1);
|
||||
texture.SetPixel(0, 0, color);
|
||||
texture.Apply();
|
||||
textureColorCache[color] = texture;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Texture2D> grayTextureCache = new Dictionary<string, Texture2D>();
|
||||
private static readonly Dictionary<string, Color> colorFactor = new Dictionary<string, Color> {
|
||||
{ "error", new Color(0.6f, 0.587f, 0.114f) },
|
||||
};
|
||||
|
||||
internal static Texture2D ConvertToGrayscale(string localIcon) {
|
||||
Texture2D _texture;
|
||||
if (!grayTextureCache.TryGetValue(localIcon, out _texture) || !_texture) {
|
||||
var icon = GUIHelper.GetLocalIcon(localIcon);
|
||||
// Create a copy of the texture
|
||||
Texture2D copiedTexture = new Texture2D(icon.width, icon.height, TextureFormat.RGBA32, false);
|
||||
|
||||
// Convert the copied texture to grayscale
|
||||
Color[] pixels = icon.GetPixels();
|
||||
for (int i = 0; i < pixels.Length; i++) {
|
||||
Color pixel = pixels[i];
|
||||
Color factor;
|
||||
if (!colorFactor.TryGetValue(localIcon, out factor)) {
|
||||
factor = new Color(0.299f, 0.587f, 0.114f);
|
||||
}
|
||||
float grayscale = factor.r * pixel.r + factor.g * pixel.g + factor.b * pixel.b;
|
||||
pixels[i] = new Color(grayscale, grayscale, grayscale, pixel.a); // Preserve alpha channel
|
||||
}
|
||||
copiedTexture.SetPixels(pixels);
|
||||
copiedTexture.Apply();
|
||||
|
||||
// Store the grayscale texture in the cache
|
||||
grayTextureCache[localIcon] = copiedTexture;
|
||||
|
||||
return copiedTexture;
|
||||
}
|
||||
return _texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4be912211814333ab61898b6440dc8e
|
||||
timeCreated: 1694518358
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,584 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Compilation;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEditor.PackageManager.Requests;
|
||||
using UnityEngine;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
|
||||
internal static class HotReloadSuggestionsHelper {
|
||||
internal static void SetSuggestionsShown(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
if (EditorPrefs.GetBool($"HotReloadWindow.SuggestionsShown.{hotReloadSuggestionKind}")) {
|
||||
return;
|
||||
}
|
||||
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", true);
|
||||
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsShown.{hotReloadSuggestionKind}", true);
|
||||
AlertEntry entry;
|
||||
if (suggestionMap.TryGetValue(hotReloadSuggestionKind, out entry) && !HotReloadTimelineHelper.Suggestions.Contains(entry)) {
|
||||
HotReloadTimelineHelper.Suggestions.Insert(0, entry);
|
||||
HotReloadState.ShowingRedDot = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool CheckSuggestionActive(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
return EditorPrefs.GetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}");
|
||||
}
|
||||
|
||||
internal static bool CheckSuggestionShown(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
return EditorPrefs.GetBool($"HotReloadWindow.SuggestionsShown.{hotReloadSuggestionKind}");
|
||||
}
|
||||
|
||||
internal static bool CanShowServerSuggestion(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerWithSideEffects) {
|
||||
return !HotReloadState.ShowedFieldInitializerWithSideEffects;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited) {
|
||||
return !HotReloadState.ShowedFieldInitializerExistingInstancesEdited;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerExistingInstancesUnedited) {
|
||||
return !HotReloadState.ShowedFieldInitializerExistingInstancesUnedited;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.AddMonobehaviourMethod) {
|
||||
return !HotReloadState.ShowedAddMonobehaviourMethods;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.DetailedErrorReportingIsEnabled) {
|
||||
return !CheckSuggestionShown(HotReloadSuggestionKind.DetailedErrorReportingIsEnabled);
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.UTF8EncodingRequired) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void SetServerSuggestionShown(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
if (hotReloadSuggestionKind == HotReloadSuggestionKind.DetailedErrorReportingIsEnabled) {
|
||||
HotReloadSuggestionsHelper.SetSuggestionsShown(hotReloadSuggestionKind);
|
||||
return;
|
||||
}
|
||||
if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerWithSideEffects) {
|
||||
HotReloadState.ShowedFieldInitializerWithSideEffects = true;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited) {
|
||||
HotReloadState.ShowedFieldInitializerExistingInstancesEdited = true;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.FieldInitializerExistingInstancesUnedited) {
|
||||
HotReloadState.ShowedFieldInitializerExistingInstancesUnedited = true;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.AddMonobehaviourMethod) {
|
||||
HotReloadState.ShowedAddMonobehaviourMethods = true;
|
||||
} else if (hotReloadSuggestionKind == HotReloadSuggestionKind.UTF8EncodingRequired) {
|
||||
// Allow showing it multiple times
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
HotReloadSuggestionsHelper.SetSuggestionActive(hotReloadSuggestionKind);
|
||||
}
|
||||
|
||||
// used for cases where suggestion might need to be shown more than once
|
||||
internal static void SetSuggestionActive(HotReloadSuggestionKind hotReloadSuggestionKind) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", false);
|
||||
AlertEntry entry;
|
||||
if (suggestionMap.TryGetValue(hotReloadSuggestionKind, out entry)) {
|
||||
HotReloadTimelineHelper.Suggestions.Remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitSuggestions() {
|
||||
foreach (HotReloadSuggestionKind value in Enum.GetValues(typeof(HotReloadSuggestionKind))) {
|
||||
if (!CheckSuggestionActive(value)) {
|
||||
continue;
|
||||
}
|
||||
AlertEntry entry;
|
||||
if (suggestionMap.TryGetValue(value, out entry) && !HotReloadTimelineHelper.Suggestions.Contains(entry)) {
|
||||
HotReloadTimelineHelper.Suggestions.Insert(0, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static HotReloadSuggestionKind? FindSuggestionKind(AlertEntry targetEntry) {
|
||||
foreach (KeyValuePair<HotReloadSuggestionKind, AlertEntry> pair in suggestionMap) {
|
||||
if (pair.Value.Equals(targetEntry)) {
|
||||
return pair.Key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static readonly OpenURLButton recompileTroubleshootingButton = new OpenURLButton(Translations.Suggestions.ButtonDocs, Constants.RecompileTroubleshootingURL);
|
||||
internal static readonly OpenURLButton featuresDocumentationButton = new OpenURLButton(Translations.Suggestions.ButtonDocs, Constants.FeaturesDocumentationURL);
|
||||
internal static readonly OpenURLButton multipleEditorsDocumentationButton = new OpenURLButton(Translations.Suggestions.ButtonDocs, Constants.MultipleEditorsURL);
|
||||
internal static readonly OpenURLButton debuggerDocumentationButton = new OpenURLButton(Translations.Suggestions.ButtonMoreInfo, Constants.DebuggerURL);
|
||||
public static Dictionary<HotReloadSuggestionKind, AlertEntry> suggestionMap = new Dictionary<HotReloadSuggestionKind, AlertEntry> {
|
||||
{ HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.Award2023Title,
|
||||
Translations.Suggestions.Award2023Message,
|
||||
actionData: () => {
|
||||
GUILayout.Space(6f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonVote)) {
|
||||
Application.OpenURL(Constants.VoteForAwardURL);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout
|
||||
)},
|
||||
{ HotReloadSuggestionKind.UnsupportedChanges, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.UnsupportedChangesTitle,
|
||||
Translations.Suggestions.UnsupportedChangesMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
featuresDocumentationButton.OnGUI();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout
|
||||
)},
|
||||
{ HotReloadSuggestionKind.UnsupportedPackages, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.UnsupportedPackagesTitle,
|
||||
Translations.Suggestions.UnsupportedPackagesMessage,
|
||||
iconType: AlertType.UnsupportedChange,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
HotReloadAboutTab.contactButton.OnGUI();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout
|
||||
)},
|
||||
{ HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.AutoRecompiledPlaymodeTitle,
|
||||
Translations.Suggestions.AutoRecompiledPlaymodeMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
recompileTroubleshootingButton.OnGUI();
|
||||
GUILayout.Space(5f);
|
||||
HotReloadAboutTab.discordButton.OnGUI();
|
||||
GUILayout.Space(5f);
|
||||
HotReloadAboutTab.contactButton.OnGUI();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout
|
||||
)},
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
{ HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.AutoRecompiled2022Title,
|
||||
Translations.Suggestions.AutoRecompiled2022Message,
|
||||
iconType: AlertType.UnsupportedChange,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonUseBuildTimeOnlyAtlas)) {
|
||||
if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2) {
|
||||
EditorSettings.spritePackerMode = SpritePackerMode.SpriteAtlasV2Build;
|
||||
} else {
|
||||
EditorSettings.spritePackerMode = SpritePackerMode.BuildTimeOnlyAtlas;
|
||||
}
|
||||
}
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOpenSettings)) {
|
||||
SettingsService.OpenProjectSettings("Project/Editor");
|
||||
}
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonIgnoreSuggestion)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022);
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
hasExitButton: false
|
||||
)},
|
||||
#endif
|
||||
{ HotReloadSuggestionKind.MultidimensionalArrays, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.MultidimensionalArraysTitle,
|
||||
Translations.Suggestions.MultidimensionalArraysMessage,
|
||||
iconType: AlertType.UnsupportedChange,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonLearnMore)) {
|
||||
string url;
|
||||
if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) {
|
||||
url = "https://learn.microsoft.com/zh-cn/dotnet/fundamentals/code-analysis/quality-rules/ca1814";
|
||||
} else {
|
||||
url = "https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1814";
|
||||
}
|
||||
Application.OpenURL(url);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout
|
||||
)},
|
||||
{ HotReloadSuggestionKind.EditorsWithoutHRRunning, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.EditorsWithoutHRTitle,
|
||||
Translations.Suggestions.EditorsWithoutHRMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonStopHotReload)) {
|
||||
EditorCodePatcher.StopCodePatcher().Forget();
|
||||
}
|
||||
GUILayout.Space(5f);
|
||||
|
||||
multipleEditorsDocumentationButton.OnGUI();
|
||||
GUILayout.Space(5f);
|
||||
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDontShowAgain)) {
|
||||
HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.EditorsWithoutHRRunning);
|
||||
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.UnsupportedChange
|
||||
)},
|
||||
// Not in use (never reported from the server)
|
||||
{ HotReloadSuggestionKind.FieldInitializerWithSideEffects, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.FieldInitializerSideEffectsTitle,
|
||||
Translations.Suggestions.FieldInitializerSideEffectsMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOK)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.FieldInitializerWithSideEffects);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDontShowAgain)) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.FieldInitializerWithSideEffects);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.FieldInitializerWithSideEffects);
|
||||
}
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
{ HotReloadSuggestionKind.DetailedErrorReportingIsEnabled, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.DetailedErrorReportingTitle,
|
||||
Translations.Suggestions.DetailedErrorReportingMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
GUILayout.Space(4f);
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOKPadded)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.DetailedErrorReportingIsEnabled);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDisable)) {
|
||||
HotReloadSettingsTab.DisableDetailedErrorReportingInner(true);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.DetailedErrorReportingIsEnabled);
|
||||
}
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
// Not in use (never reported from the server)
|
||||
{ HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.FieldInitializerEditedTitle,
|
||||
Translations.Suggestions.FieldInitializerEditedMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonTurnOff)) {
|
||||
#pragma warning disable CS0618
|
||||
HotReloadSettingsTab.ApplyApplyFieldInitializerEditsToExistingClassInstances(false);
|
||||
#pragma warning restore CS0618
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited);
|
||||
}
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOpenSettings)) {
|
||||
HotReloadWindow.Current.SelectTab(typeof(HotReloadSettingsTab));
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDontShowAgain)) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.FieldInitializerExistingInstancesEdited);
|
||||
}
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
{ HotReloadSuggestionKind.FieldInitializerExistingInstancesUnedited, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.FieldInitializerUneditedTitle,
|
||||
Translations.Suggestions.FieldInitializerUneditedMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(8f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOK)) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.FieldInitializerExistingInstancesUnedited);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.FieldInitializerExistingInstancesUnedited);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
{ HotReloadSuggestionKind.AddMonobehaviourMethod, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.AddMonobehaviourMethodTitle,
|
||||
Translations.Suggestions.AddMonobehaviourMethodMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(8f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOK)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.AddMonobehaviourMethod);
|
||||
}
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonAutoRecompile)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.AddMonobehaviourMethod);
|
||||
HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges = true;
|
||||
HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported = true;
|
||||
HotReloadRunTab.RecompileWithChecks();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDontShowAgain)) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.AddMonobehaviourMethod);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.AddMonobehaviourMethod);
|
||||
}
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
{ HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.SwitchToDebugModeTitle,
|
||||
Translations.Suggestions.SwitchToDebugModeMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonSwitchToDebugMode) && HotReloadRunTab.ConfirmExitPlaymode(Translations.Suggestions.SwitchToDebugModeConfirmation)) {
|
||||
HotReloadRunTab.SwitchToDebugMode();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.UnsupportedChange
|
||||
)},
|
||||
#endif
|
||||
{ HotReloadSuggestionKind.HotReloadWhileDebuggerIsAttached, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.DebuggerAttachedTitle,
|
||||
Translations.Suggestions.DebuggerAttachedMessagePaused,
|
||||
actionData: () => {
|
||||
GUILayout.Space(8f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonKeepEnabledDuringDebugging)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.HotReloadWhileDebuggerIsAttached);
|
||||
HotReloadPrefs.AutoDisableHotReloadWithDebugger = false;
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
debuggerDocumentationButton.OnGUI();
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonDontShowAgain)) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.HotReloadWhileDebuggerIsAttached);
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.HotReloadWhileDebuggerIsAttached);
|
||||
}
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.Suggestion
|
||||
)},
|
||||
{ HotReloadSuggestionKind.HotReloadedMethodsWhenDebuggerIsAttached, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.DebuggerMethodsTitle,
|
||||
Translations.Suggestions.DebuggerMethodsMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(8f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonRecompile)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.HotReloadedMethodsWhenDebuggerIsAttached);
|
||||
if (HotReloadRunTab.ConfirmExitPlaymode(Translations.Suggestions.DebuggerMethodsConfirmation)) {
|
||||
HotReloadRunTab.Recompile();
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
debuggerDocumentationButton.OnGUI();
|
||||
GUILayout.Space(8f);
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.UnsupportedChange,
|
||||
hasExitButton: false
|
||||
)},
|
||||
{ HotReloadSuggestionKind.UTF8EncodingRequired, new AlertEntry(
|
||||
AlertType.Suggestion,
|
||||
Translations.Suggestions.UTF8EncodingRequiredTitle,
|
||||
Translations.Suggestions.UTF8EncodingRequiredMessage,
|
||||
actionData: () => {
|
||||
GUILayout.Space(8f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (GUILayout.Button(Translations.Suggestions.ButtonOK)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.UTF8EncodingRequired);
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
},
|
||||
timestamp: DateTime.Now,
|
||||
entryType: EntryType.Foldout,
|
||||
iconType: AlertType.UnsupportedChange,
|
||||
hasExitButton: false
|
||||
)},
|
||||
};
|
||||
|
||||
static ListRequest listRequest;
|
||||
static string[] unsupportedPackages = new[] {
|
||||
"com.unity.entities",
|
||||
"com.firstgeargames.fishnet",
|
||||
};
|
||||
static List<string> unsupportedPackagesList;
|
||||
static DateTime lastPlaymodeChange;
|
||||
|
||||
public static void Init() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
listRequest = Client.List(offlineMode: false, includeIndirectDependencies: true);
|
||||
|
||||
EditorApplication.playModeStateChanged += state => {
|
||||
lastPlaymodeChange = DateTime.UtcNow;
|
||||
};
|
||||
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 (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
if (listRequest.IsCompleted &&
|
||||
unsupportedPackagesList == null)
|
||||
{
|
||||
unsupportedPackagesList = new List<string>();
|
||||
if (listRequest.Result != null) {
|
||||
foreach (var packageInfo in listRequest.Result) {
|
||||
if (unsupportedPackages.Contains(packageInfo.name)) {
|
||||
unsupportedPackagesList.Add(packageInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unsupportedPackagesList.Count > 0) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.UnsupportedPackages);
|
||||
}
|
||||
}
|
||||
|
||||
CheckEditorsWithoutHR();
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
if (EditorSettings.spritePackerMode == SpritePackerMode.AlwaysOnAtlas || EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2) {
|
||||
SetSuggestionsShown(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022);
|
||||
} else if (CheckSuggestionActive(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022)) {
|
||||
SetSuggestionInactive(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022);
|
||||
EditorPrefs.SetBool($"HotReloadWindow.SuggestionsShown.{HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022}", false);
|
||||
}
|
||||
#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 editorsWithoutHr = await RequestHelper.RequestEditorsWithoutHRRunning();
|
||||
if (editorsWithoutHr == null) {
|
||||
return;
|
||||
}
|
||||
var showSuggestion = editorsWithoutHr.editorsWithoutHRRunning;
|
||||
if (!showSuggestion) {
|
||||
HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
|
||||
return;
|
||||
}
|
||||
if (!HotReloadState.ShowedEditorsWithoutHR && ServerHealthCheck.I.IsServerHealthy) {
|
||||
HotReloadSuggestionsHelper.SetSuggestionActive(HotReloadSuggestionKind.EditorsWithoutHRRunning);
|
||||
}
|
||||
} finally {
|
||||
checkingEditorsWihtoutHR = false;
|
||||
lastCheckedUnityInstances = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cc471e812b143599ef5dde1d7ec022a
|
||||
timeCreated: 1694632601
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,608 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal enum TimelineType {
|
||||
Suggestions,
|
||||
Timeline,
|
||||
}
|
||||
|
||||
internal enum AlertType {
|
||||
Suggestion,
|
||||
UnsupportedChange,
|
||||
CompileError,
|
||||
PartiallySupportedChange,
|
||||
AppliedChange,
|
||||
UndetectedChange,
|
||||
}
|
||||
|
||||
internal enum AlertEntryType {
|
||||
Error,
|
||||
Failure,
|
||||
InlinedMethod,
|
||||
PatchApplied,
|
||||
PartiallySupportedChange,
|
||||
UndetectedChange,
|
||||
}
|
||||
|
||||
internal enum EntryType {
|
||||
Parent,
|
||||
Child,
|
||||
Standalone,
|
||||
Foldout,
|
||||
}
|
||||
|
||||
internal class PersistedAlertData {
|
||||
public readonly AlertData[] alertDatas;
|
||||
|
||||
public PersistedAlertData(AlertData[] alertDatas) {
|
||||
this.alertDatas = alertDatas;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AlertData {
|
||||
public readonly AlertEntryType alertEntryType;
|
||||
public readonly string errorString;
|
||||
public readonly string methodName;
|
||||
public readonly string methodSimpleName;
|
||||
public readonly PartiallySupportedChange partiallySupportedChange;
|
||||
public readonly EntryType entryType;
|
||||
public readonly bool detiled;
|
||||
public readonly DateTime createdAt;
|
||||
public readonly string[] patchedMembersDisplayNames;
|
||||
|
||||
public AlertData(AlertEntryType alertEntryType, DateTime createdAt, bool detiled = false, EntryType entryType = EntryType.Standalone, string errorString = null, string methodName = null, string methodSimpleName = null, PartiallySupportedChange partiallySupportedChange = default(PartiallySupportedChange), string[] patchedMembersDisplayNames = null) {
|
||||
this.alertEntryType = alertEntryType;
|
||||
this.createdAt = createdAt;
|
||||
this.detiled = detiled;
|
||||
this.entryType = entryType;
|
||||
this.errorString = errorString;
|
||||
this.methodName = methodName;
|
||||
this.methodSimpleName = methodSimpleName;
|
||||
this.partiallySupportedChange = partiallySupportedChange;
|
||||
this.patchedMembersDisplayNames = patchedMembersDisplayNames;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AlertEntry {
|
||||
internal readonly AlertType alertType;
|
||||
internal readonly string title;
|
||||
internal readonly DateTime timestamp;
|
||||
internal readonly string description;
|
||||
[CanBeNull] internal readonly Action actionData;
|
||||
internal readonly AlertType iconType;
|
||||
internal readonly string shortDescription;
|
||||
internal readonly EntryType entryType;
|
||||
internal readonly AlertData alertData;
|
||||
internal readonly bool hasExitButton;
|
||||
|
||||
internal AlertEntry(AlertType alertType, string title, string description, DateTime timestamp, string shortDescription = null, Action actionData = null, AlertType? iconType = null, EntryType entryType = EntryType.Standalone, AlertData alertData = default(AlertData), bool hasExitButton = true) {
|
||||
this.alertType = alertType;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.shortDescription = shortDescription;
|
||||
this.actionData = actionData;
|
||||
this.iconType = iconType ?? alertType;
|
||||
this.timestamp = timestamp;
|
||||
this.entryType = entryType;
|
||||
this.alertData = alertData;
|
||||
this.hasExitButton = hasExitButton;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class HotReloadTimelineHelper {
|
||||
internal const int maxVisibleEntries = 40;
|
||||
|
||||
private static List<AlertEntry> eventsTimeline = new List<AlertEntry>();
|
||||
internal static List<AlertEntry> EventsTimeline => eventsTimeline;
|
||||
|
||||
static readonly string filePath = Path.Combine(PackageConst.LibraryCachePath, "eventEntries.json");
|
||||
|
||||
public static async Task InitPersistedEvents() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
var redDotShown = HotReloadState.ShowingRedDot;
|
||||
try {
|
||||
var persistedAlertData = await Task.Run(() => JsonConvert.DeserializeObject<PersistedAlertData>(File.ReadAllText(filePath)));
|
||||
eventsTimeline = new List<AlertEntry>(persistedAlertData.alertDatas.Length);
|
||||
for (int i = persistedAlertData.alertDatas.Length - 1; i >= 0; i--) {
|
||||
AlertData alertData = persistedAlertData.alertDatas[i];
|
||||
switch (alertData.alertEntryType) {
|
||||
case AlertEntryType.Error:
|
||||
CreateErrorEventEntry(errorString: alertData.errorString, entryType: alertData.entryType, createdAt: alertData.createdAt);
|
||||
break;
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
case AlertEntryType.InlinedMethod:
|
||||
CreateInlinedMethodsEntry(alertData.patchedMembersDisplayNames, alertData.entryType, alertData.createdAt);
|
||||
break;
|
||||
#endif
|
||||
case AlertEntryType.Failure:
|
||||
if (alertData.entryType == EntryType.Parent) {
|
||||
CreateReloadFinishedWithWarningsEventEntry(createdAt: alertData.createdAt, patchedMembersDisplayNames: alertData.patchedMembersDisplayNames);
|
||||
} else {
|
||||
CreatePatchFailureEventEntry(errorString: alertData.errorString, methodName: alertData.methodName, methodSimpleName: alertData.methodSimpleName, entryType: alertData.entryType, createdAt: alertData.createdAt);
|
||||
}
|
||||
break;
|
||||
case AlertEntryType.PatchApplied:
|
||||
CreateReloadFinishedEventEntry(
|
||||
createdAt: alertData.createdAt,
|
||||
patchedMethodsDisplayNames: alertData.patchedMembersDisplayNames
|
||||
);
|
||||
break;
|
||||
case AlertEntryType.PartiallySupportedChange:
|
||||
if (alertData.entryType == EntryType.Parent) {
|
||||
CreateReloadPartiallyAppliedEventEntry(createdAt: alertData.createdAt, patchedMethodsDisplayNames: alertData.patchedMembersDisplayNames);
|
||||
} else {
|
||||
CreatePartiallyAppliedEventEntry(alertData.partiallySupportedChange, entryType: alertData.entryType, detailed: alertData.detiled, createdAt: alertData.createdAt);
|
||||
}
|
||||
break;
|
||||
case AlertEntryType.UndetectedChange:
|
||||
CreateReloadUndetectedChangeEventEntry(createdAt: alertData.createdAt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Warning(Translations.Errors.WarningInitializingEventEntries, e);
|
||||
} finally {
|
||||
// Ensure red dot is not triggered for existing entries
|
||||
HotReloadState.ShowingRedDot = redDotShown;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task PersistTimeline() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
var persistedData = new PersistedAlertData(eventsTimeline.Where(x => x.alertType != AlertType.CompileError).Select(x => x.alertData).ToArray());
|
||||
try {
|
||||
await Task.Run(() => File.WriteAllText(path: filePath, contents: JsonConvert.SerializeObject(persistedData)));
|
||||
} catch (Exception e) {
|
||||
Log.Warning(Translations.Errors.WarningPersistingEventEntries, e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearPersistance() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
Task.Run(() => File.Delete(filePath));
|
||||
eventsTimeline = new List<AlertEntry>();
|
||||
}
|
||||
|
||||
internal static readonly Dictionary<AlertType, string> alertIconString = new Dictionary<AlertType, string> {
|
||||
{ AlertType.Suggestion, "alert_info" },
|
||||
{ AlertType.UnsupportedChange, "warning" },
|
||||
{ AlertType.CompileError, "error" },
|
||||
{ AlertType.PartiallySupportedChange, "infos" },
|
||||
{ AlertType.AppliedChange, "applied_patch" },
|
||||
{ AlertType.UndetectedChange, "undetected" },
|
||||
};
|
||||
|
||||
#pragma warning disable CS0612 // obsolete
|
||||
public static Dictionary<PartiallySupportedChange, string> partiallySupportedChangeDescriptions => new Dictionary<PartiallySupportedChange, string> {
|
||||
{PartiallySupportedChange.LambdaClosure, Translations.Timeline.PartiallySupportedLambdaClosure},
|
||||
{PartiallySupportedChange.EditAsyncMethod, Translations.Timeline.PartiallySupportedEditAsyncMethod},
|
||||
{PartiallySupportedChange.AddMonobehaviourMethod, Translations.Timeline.PartiallySupportedAddMonobehaviourMethod},
|
||||
{PartiallySupportedChange.EditMonobehaviourField, Translations.Timeline.PartiallySupportedEditMonobehaviourField},
|
||||
{PartiallySupportedChange.EditCoroutine, Translations.Timeline.PartiallySupportedEditCoroutine},
|
||||
{PartiallySupportedChange.EditGenericFieldInitializer, Translations.Timeline.PartiallySupportedEditGenericFieldInitializer},
|
||||
{PartiallySupportedChange.AddEnumMember, Translations.Timeline.PartiallySupportedAddEnumMember},
|
||||
{PartiallySupportedChange.EditFieldInitializer, Translations.Timeline.PartiallySupportedEditFieldInitializer},
|
||||
{PartiallySupportedChange.AddMethodWithAttributes, Translations.Timeline.PartiallySupportedAddMethodWithAttributes},
|
||||
{PartiallySupportedChange.AddFieldWithAttributes, Translations.Timeline.PartiallySupportedAddFieldWithAttributes},
|
||||
{PartiallySupportedChange.GenericMethodInGenericClass, Translations.Timeline.PartiallySupportedGenericMethodInGenericClass},
|
||||
{PartiallySupportedChange.NewCustomSerializableField, Translations.Timeline.PartiallySupportedNewCustomSerializableField},
|
||||
{PartiallySupportedChange.MultipleFieldsEditedInTheSameType, Translations.Timeline.PartiallySupportedMultipleFieldsEditedInTheSameType},
|
||||
};
|
||||
#pragma warning restore CS0612
|
||||
|
||||
internal static List<AlertEntry> Suggestions = new List<AlertEntry>();
|
||||
internal static int UnsupportedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.UnsupportedChange && alert.entryType != EntryType.Child);
|
||||
internal static int PartiallySupportedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.PartiallySupportedChange && alert.entryType != EntryType.Child);
|
||||
internal static int UndetectedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.UndetectedChange && alert.entryType != EntryType.Child);
|
||||
internal static int CompileErrorsCount => EventsTimeline.Count(alert => alert.alertType == AlertType.CompileError);
|
||||
internal static int AppliedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.AppliedChange);
|
||||
|
||||
static Regex shortDescriptionRegex = new Regex(PackageConst.DefaultLocale == Locale.SimplifiedChinese ? @"^([\p{L}\p{N}_]+)\s([\p{L}\p{N}_]+)(?=:)" : @"^(\w+)\s(\w+)(?=:)", RegexOptions.Compiled);
|
||||
|
||||
internal static int GetRunTabTimelineEventCount() {
|
||||
int total = 0;
|
||||
if (HotReloadPrefs.RunTabUnsupportedChangesFilter) {
|
||||
total += UnsupportedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter) {
|
||||
total += PartiallySupportedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabUndetectedPatchesFilter) {
|
||||
total += UndetectedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabCompileErrorFilter) {
|
||||
total += CompileErrorsCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabAppliedPatchesFilter) {
|
||||
total += AppliedChangesCount;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
internal static List<AlertEntry> expandedEntries = new List<AlertEntry>();
|
||||
|
||||
internal static void RenderCompileButton() {
|
||||
if (GUILayout.Button(Translations.Common.ButtonRecompile.Trim(), GUILayout.Width(80))) {
|
||||
HotReloadRunTab.RecompileWithChecks();
|
||||
}
|
||||
}
|
||||
|
||||
private static float maxScrollPos;
|
||||
internal static void RenderErrorEventActions(string description, ErrorData errorData) {
|
||||
int maxLen = 2400;
|
||||
string text = errorData.stacktrace;
|
||||
if (text.Length > maxLen) {
|
||||
text = text.Substring(0, maxLen) + "...";
|
||||
}
|
||||
|
||||
GUILayout.TextArea(text, HotReloadWindowStyles.StacktraceTextAreaStyle);
|
||||
|
||||
if (errorData.file || !errorData.stacktrace.Contains("error CS")) {
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (!errorData.stacktrace.Contains("error CS")) {
|
||||
RenderCompileButton();
|
||||
}
|
||||
|
||||
// Link
|
||||
if (errorData.file) {
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(errorData.linkString, HotReloadWindowStyles.LinkStyle)) {
|
||||
AssetDatabase.OpenAsset(errorData.file, Math.Max(errorData.lineNumber, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Texture2D GetFilterIcon(int count, AlertType alertType) {
|
||||
if (count == 0) {
|
||||
return GUIHelper.ConvertToGrayscale(alertIconString[alertType]);
|
||||
}
|
||||
return GUIHelper.GetLocalIcon(alertIconString[alertType]);
|
||||
}
|
||||
|
||||
internal static void RenderAlertFilters() {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
var text = AppliedChangesCount > 999 ? "999+" : " " + AppliedChangesCount;
|
||||
|
||||
HotReloadPrefs.RunTabAppliedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabAppliedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(AppliedChangesCount, AlertType.AppliedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = UndetectedChangesCount > 999 ? "999+" : " " + UndetectedChangesCount;
|
||||
HotReloadPrefs.RunTabUndetectedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabUndetectedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(UnsupportedChangesCount, AlertType.UndetectedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = PartiallySupportedChangesCount > 999 ? "999+" : " " + PartiallySupportedChangesCount;
|
||||
HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(PartiallySupportedChangesCount, AlertType.PartiallySupportedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = UnsupportedChangesCount > 999 ? "999+" : " " + UnsupportedChangesCount;
|
||||
HotReloadPrefs.RunTabUnsupportedChangesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabUnsupportedChangesFilter,
|
||||
new GUIContent(text, GetFilterIcon(UnsupportedChangesCount, AlertType.UnsupportedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = CompileErrorsCount > 999 ? "999+" : " " + CompileErrorsCount;
|
||||
HotReloadPrefs.RunTabCompileErrorFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabCompileErrorFilter,
|
||||
new GUIContent(text, GetFilterIcon(CompileErrorsCount, AlertType.CompileError)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateErrorEventEntry(string errorString, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var alertType = errorString.Contains("error CS")
|
||||
? AlertType.CompileError
|
||||
: AlertType.UnsupportedChange;
|
||||
var title = errorString.Contains("error CS")
|
||||
? Translations.Utility.CompileError
|
||||
: Translations.Utility.UnsupportedChange;
|
||||
ErrorData errorData = ErrorData.GetErrorData(errorString);
|
||||
var description = errorData.error;
|
||||
string shortDescription = null;
|
||||
if (alertType != AlertType.CompileError) {
|
||||
shortDescription = shortDescriptionRegex.Match(description).Value;
|
||||
}
|
||||
Action actionData = () => RenderErrorEventActions(description, errorData);
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: alertType,
|
||||
title: title,
|
||||
description: description,
|
||||
shortDescription: shortDescription,
|
||||
actionData: actionData,
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.Error, createdAt: timestamp, errorString: errorString, entryType: entryType)
|
||||
));
|
||||
}
|
||||
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
internal static void CreateInlinedMethodsEntry(string[] patchedMethodsDisplayNames, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UnsupportedChange,
|
||||
title: Translations.Timeline.EventTitleFailedApplyingPatch,
|
||||
description: $"{Translations.Timeline.EventDescriptionInlinedMethods}\n\n• {(truncated ? patchesList + "\n..." : patchesList)}",
|
||||
entryType: EntryType.Parent,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
var suggestion = HotReloadSuggestionsHelper.suggestionMap[HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods];
|
||||
if (suggestion?.actionData != null) {
|
||||
suggestion.actionData();
|
||||
}
|
||||
}
|
||||
},
|
||||
alertData: new AlertData(AlertEntryType.InlinedMethod, createdAt: timestamp, patchedMembersDisplayNames: patchedMethodsDisplayNames, entryType: EntryType.Parent)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void CreatePatchFailureEventEntry(string errorString, string methodName, string methodSimpleName = null, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
ErrorData errorData = ErrorData.GetErrorData(errorString);
|
||||
var title = Translations.Timeline.EventTitleFailedApplyingPatch;
|
||||
Action actionData = () => RenderErrorEventActions(errorData.error, errorData);
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UnsupportedChange,
|
||||
title: title,
|
||||
description: string.Format(Translations.Timeline.EventDescriptionFailedApplyingPatchTapForMore, title, methodName),
|
||||
shortDescription: methodSimpleName,
|
||||
actionData: actionData,
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, errorString: errorString, methodName: methodName, methodSimpleName: methodSimpleName, entryType: entryType)
|
||||
));
|
||||
}
|
||||
|
||||
public static T[] TruncateList<T>(T[] originalList, int len) {
|
||||
if (originalList.Length <= len) {
|
||||
return originalList;
|
||||
}
|
||||
// Create a new list with a maximum of 25 items
|
||||
T[] truncatedList = new T[len];
|
||||
|
||||
for (int i = 0; i < originalList.Length && i < len; i++) {
|
||||
truncatedList[i] = originalList[i];
|
||||
}
|
||||
|
||||
return truncatedList;
|
||||
}
|
||||
|
||||
internal static void CreateReloadFinishedEventEntry(DateTime? createdAt = null, string[] patchedMethodsDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.AppliedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Reloaded],
|
||||
description: patchedMethodsDisplayNames?.Length > 0
|
||||
? $"• {(truncated ? patchesList + "\n..." : patchesList)}"
|
||||
: Translations.Timeline.EventDescriptionNoIssuesFound,
|
||||
entryType: patchedMethodsDisplayNames?.Length > 0 ? EntryType.Parent : EntryType.Standalone,
|
||||
alertData: new AlertData(
|
||||
AlertEntryType.PatchApplied,
|
||||
createdAt: timestamp,
|
||||
entryType: EntryType.Standalone,
|
||||
patchedMembersDisplayNames: patchedMethodsDisplayNames)
|
||||
);
|
||||
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadFinishedWithWarningsEventEntry(DateTime? createdAt = null, string[] patchedMembersDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMembersDisplayNames?.Length > 25) {
|
||||
patchedMembersDisplayNames = TruncateList(patchedMembersDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMembersDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMembersDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.UnsupportedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Unsupported],
|
||||
description: patchedMembersDisplayNames?.Length > 0 ? $"• {(truncated ? patchesList + "\n...\n\n" + Translations.Timeline.EventDescriptionSeeUnsupportedChangesBelow : patchesList + "\n\n" + Translations.Timeline.EventDescriptionSeeUnsupportedChangesBelow)}" : Translations.Timeline.EventDescriptionSeeDetailedEntriesBelow,
|
||||
entryType: EntryType.Parent,
|
||||
alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, entryType: EntryType.Parent, patchedMembersDisplayNames: patchedMembersDisplayNames)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMembersDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadPartiallyAppliedEventEntry(DateTime? createdAt = null, string[] patchedMethodsDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.PartiallySupportedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.PartiallySupported],
|
||||
description: patchedMethodsDisplayNames?.Length > 0 ? $"• {(truncated ? patchesList + "\n...\n\n" + Translations.Timeline.EventDescriptionSeePartiallyAppliedChangesBelow : patchesList + "\n\n" + Translations.Timeline.EventDescriptionSeePartiallyAppliedChangesBelow)}" : Translations.Timeline.EventDescriptionSeeDetailedEntriesBelow,
|
||||
entryType: EntryType.Parent,
|
||||
alertData: new AlertData(AlertEntryType.PartiallySupportedChange, createdAt: timestamp, entryType: EntryType.Parent, patchedMembersDisplayNames: patchedMethodsDisplayNames)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadUndetectedChangeEventEntry(DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UndetectedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Undetected],
|
||||
description: Translations.Timeline.EventDescriptionUndetectedChange,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
GUILayout.FlexibleSpace();
|
||||
OpenURLButton.Render(Translations.Suggestions.ButtonDocs, Constants.UndetectedChangesURL);
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
},
|
||||
entryType: EntryType.Foldout,
|
||||
alertData: new AlertData(AlertEntryType.UndetectedChange, createdAt: timestamp, entryType: EntryType.Parent)
|
||||
));
|
||||
}
|
||||
|
||||
internal static void CreatePartiallyAppliedEventEntry(PartiallySupportedChange partiallySupportedChange, EntryType entryType = EntryType.Standalone, bool detailed = true, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
string description;
|
||||
if (!partiallySupportedChangeDescriptions.TryGetValue(partiallySupportedChange, out description)) {
|
||||
return;
|
||||
}
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.PartiallySupportedChange,
|
||||
title : detailed ? Translations.Timeline.EventTitleChangePartiallyApplied : ToString(partiallySupportedChange),
|
||||
description : description,
|
||||
shortDescription: detailed ? ToString(partiallySupportedChange) : null,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GetPartiallySupportedChangePref(partiallySupportedChange)) {
|
||||
if (GUILayout.Button(Translations.Timeline.ButtonIgnoreEventType, HotReloadWindowStyles.LinkStyle)) {
|
||||
HidePartiallySupportedChange(partiallySupportedChange);
|
||||
HotReloadRunTab.RepaintInstant();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.PartiallySupportedChange, createdAt: timestamp, partiallySupportedChange: partiallySupportedChange, entryType: entryType, detiled: detailed)
|
||||
));
|
||||
}
|
||||
|
||||
internal static void InsertEntry(AlertEntry entry) {
|
||||
eventsTimeline.Insert(0, entry);
|
||||
if (entry.alertType != AlertType.AppliedChange) {
|
||||
HotReloadState.ShowingRedDot = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearEntries() {
|
||||
eventsTimeline.Clear();
|
||||
}
|
||||
|
||||
internal static bool GetPartiallySupportedChangePref(PartiallySupportedChange key) {
|
||||
return EditorPrefs.GetBool($"HotReloadWindow.ShowPartiallySupportedChangeType.{key}", true);
|
||||
}
|
||||
|
||||
internal static void HidePartiallySupportedChange(PartiallySupportedChange key) {
|
||||
EditorPrefs.SetBool($"HotReloadWindow.ShowPartiallySupportedChangeType.{key}", false);
|
||||
// loop over scroll entries to remove hidden entries
|
||||
for (var i = EventsTimeline.Count - 1; i >= 0; i--) {
|
||||
var eventEntry = EventsTimeline[i];
|
||||
if (eventEntry.alertData.partiallySupportedChange == key) {
|
||||
EventsTimeline.Remove(eventEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// performance optimization (Enum.ToString uses reflection)
|
||||
internal static string ToString(this PartiallySupportedChange change) {
|
||||
#pragma warning disable CS0612 // obsolete
|
||||
switch (change) {
|
||||
case PartiallySupportedChange.LambdaClosure:
|
||||
return nameof(PartiallySupportedChange.LambdaClosure);
|
||||
case PartiallySupportedChange.EditAsyncMethod:
|
||||
return nameof(PartiallySupportedChange.EditAsyncMethod);
|
||||
case PartiallySupportedChange.AddMonobehaviourMethod:
|
||||
return nameof(PartiallySupportedChange.AddMonobehaviourMethod);
|
||||
case PartiallySupportedChange.EditMonobehaviourField:
|
||||
return nameof(PartiallySupportedChange.EditMonobehaviourField);
|
||||
case PartiallySupportedChange.EditCoroutine:
|
||||
return nameof(PartiallySupportedChange.EditCoroutine);
|
||||
case PartiallySupportedChange.EditGenericFieldInitializer:
|
||||
return nameof(PartiallySupportedChange.EditGenericFieldInitializer);
|
||||
case PartiallySupportedChange.AddEnumMember:
|
||||
return nameof(PartiallySupportedChange.AddEnumMember);
|
||||
case PartiallySupportedChange.EditFieldInitializer:
|
||||
return nameof(PartiallySupportedChange.EditFieldInitializer);
|
||||
case PartiallySupportedChange.AddMethodWithAttributes:
|
||||
return nameof(PartiallySupportedChange.AddMethodWithAttributes);
|
||||
case PartiallySupportedChange.GenericMethodInGenericClass:
|
||||
return nameof(PartiallySupportedChange.GenericMethodInGenericClass);
|
||||
case PartiallySupportedChange.AddFieldWithAttributes:
|
||||
return nameof(PartiallySupportedChange.AddFieldWithAttributes);
|
||||
case PartiallySupportedChange.NewCustomSerializableField:
|
||||
return nameof(PartiallySupportedChange.NewCustomSerializableField);
|
||||
case PartiallySupportedChange.MultipleFieldsEditedInTheSameType:
|
||||
return nameof(PartiallySupportedChange.MultipleFieldsEditedInTheSameType);
|
||||
#pragma warning restore CS0612
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(change), change, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffb65be71b8b4d14800f8b28bf68d0ab
|
||||
timeCreated: 1695210350
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal class Spinner {
|
||||
internal static string SpinnerIconPath => "icon_loading_star_light_mode_96";
|
||||
internal static Texture2D spinnerTexture => GUIHelper.GetInvertibleIcon(InvertibleIcon.Spinner);
|
||||
private Texture2D _rotatedTextureLight;
|
||||
private Texture2D _rotatedTextureDark;
|
||||
private Texture2D rotatedTextureLight => _rotatedTextureLight ? _rotatedTextureLight : _rotatedTextureLight = GetCopy(spinnerTexture);
|
||||
private Texture2D rotatedTextureDark => _rotatedTextureDark ? _rotatedTextureDark : _rotatedTextureDark = GetCopy(spinnerTexture);
|
||||
internal Texture2D rotatedTexture => HotReloadWindowStyles.IsDarkMode ? rotatedTextureDark : rotatedTextureLight;
|
||||
|
||||
private float _rotationAngle;
|
||||
private DateTime _lastRotation;
|
||||
private int _rotationPeriod;
|
||||
|
||||
internal Spinner(int rotationPeriodInMilliseconds) {
|
||||
_rotationPeriod = rotationPeriodInMilliseconds;
|
||||
}
|
||||
|
||||
internal Texture2D GetIcon() {
|
||||
if (DateTime.UtcNow - _lastRotation > TimeSpan.FromMilliseconds(_rotationPeriod)) {
|
||||
_lastRotation = DateTime.UtcNow;
|
||||
_rotationAngle += 45;
|
||||
if (_rotationAngle >= 360f)
|
||||
_rotationAngle -= 360f;
|
||||
return RotateImage(spinnerTexture, _rotationAngle);
|
||||
}
|
||||
return rotatedTexture;
|
||||
}
|
||||
|
||||
private Texture2D RotateImage(Texture2D originalTexture, float angle) {
|
||||
int w = originalTexture.width;
|
||||
int h = originalTexture.height;
|
||||
|
||||
int x, y;
|
||||
float centerX = w / 2f;
|
||||
float centerY = h / 2f;
|
||||
|
||||
for (x = 0; x < w; x++) {
|
||||
for (y = 0; y < h; y++) {
|
||||
float dx = x - centerX;
|
||||
float dy = y - centerY;
|
||||
float distance = Mathf.Sqrt(dx * dx + dy * dy);
|
||||
float oldAngle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg;
|
||||
float newAngle = oldAngle + angle;
|
||||
|
||||
float newX = centerX + distance * Mathf.Cos(newAngle * Mathf.Deg2Rad);
|
||||
float newY = centerY + distance * Mathf.Sin(newAngle * Mathf.Deg2Rad);
|
||||
|
||||
if (newX >= 0 && newX < w && newY >= 0 && newY < h) {
|
||||
rotatedTexture.SetPixel(x, y, originalTexture.GetPixel((int)newX, (int)newY));
|
||||
} else {
|
||||
rotatedTexture.SetPixel(x, y, Color.clear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rotatedTexture.Apply();
|
||||
return rotatedTexture;
|
||||
}
|
||||
|
||||
public static Texture2D GetCopy(Texture2D tex, TextureFormat format = TextureFormat.RGBA32, bool mipChain = false) {
|
||||
var tmp = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
|
||||
Graphics.Blit(tex, tmp);
|
||||
|
||||
RenderTexture.active = tmp;
|
||||
try {
|
||||
var copy = new Texture2D(tex.width, tex.height, format, mipChain: mipChain);
|
||||
copy.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
|
||||
copy.Apply();
|
||||
return copy;
|
||||
} finally {
|
||||
RenderTexture.active = null;
|
||||
RenderTexture.ReleaseTemporary(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bd77f0465824c5da3e1454f75c6e93c
|
||||
timeCreated: 1685871830
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,95 @@
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("SingularityGroup.HotReload.Demo")]
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal class UnitySettingsHelper {
|
||||
public static UnitySettingsHelper I = new UnitySettingsHelper();
|
||||
|
||||
private bool initialized;
|
||||
private object pref;
|
||||
private PropertyInfo prefColorProp;
|
||||
private MethodInfo setMethod;
|
||||
private Type settingsType;
|
||||
private Type prefColorType;
|
||||
const string currentPlaymodeTintPrefKey = "Playmode tint";
|
||||
|
||||
internal bool playmodeTintSupported => EditorCodePatcher.config.changePlaymodeTint && EnsureInitialized();
|
||||
|
||||
private UnitySettingsHelper() {
|
||||
EnsureInitialized();
|
||||
}
|
||||
|
||||
|
||||
private bool EnsureInitialized() {
|
||||
if (initialized) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
// cache members for performance
|
||||
settingsType = settingsType ?? (settingsType = typeof(UnityEditor.Editor).Assembly.GetType($"UnityEditor.PrefSettings"));
|
||||
prefColorType = prefColorType ?? (prefColorType = typeof(UnityEditor.Editor).Assembly.GetType($"UnityEditor.PrefColor"));
|
||||
prefColorProp = prefColorProp ?? (prefColorProp = prefColorType?.GetProperty("Color", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
|
||||
pref = pref ?? (pref = GetPref(settingsType: settingsType, prefColorType: prefColorType));
|
||||
setMethod = setMethod ?? (setMethod = GetSetMethod(settingsType: settingsType, prefColorType: prefColorType));
|
||||
|
||||
if (prefColorProp == null
|
||||
|| pref == null
|
||||
|| setMethod == null
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear cache for performance
|
||||
settingsType = null;
|
||||
prefColorType = null;
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo GetSetMethod(Type settingsType, Type prefColorType) {
|
||||
var setMethodBase = settingsType?.GetMethod("Set", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
return setMethodBase?.MakeGenericMethod(prefColorType);
|
||||
}
|
||||
|
||||
private static object GetPref(Type settingsType, Type prefColorType) {
|
||||
var prefsMethodBase = settingsType?.GetMethod("Prefs", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var prefsMethod = prefsMethodBase?.MakeGenericMethod(prefColorType);
|
||||
var prefs = (IEnumerable)prefsMethod?.Invoke(null, Array.Empty<object>());
|
||||
if (prefs != null) {
|
||||
foreach (object kvp in prefs) {
|
||||
var key = kvp.GetType().GetProperty("Key", BindingFlags.Instance | BindingFlags.Public)?.GetMethod.Invoke(kvp, Array.Empty<object>());
|
||||
if (key?.ToString() == currentPlaymodeTintPrefKey) {
|
||||
return kvp.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)?.GetMethod.Invoke(kvp, Array.Empty<object>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Color? GetCurrentPlaymodeColor() {
|
||||
if (!playmodeTintSupported) {
|
||||
return null;
|
||||
}
|
||||
return (Color)prefColorProp.GetValue(pref);
|
||||
}
|
||||
|
||||
public void SetPlaymodeTint(Color color) {
|
||||
if (!playmodeTintSupported) {
|
||||
return;
|
||||
}
|
||||
prefColorProp.SetValue(pref, color);
|
||||
setMethod.Invoke(null, new object[] { currentPlaymodeTintPrefKey, pref });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34fb1222dc00466ab4e3db7383bd00ee
|
||||
timeCreated: 1694279476
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,35 @@
|
||||
#if ODIN_INSPECTOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using SingularityGroup.HotReload.EditorDependencies;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
public class HotReloadAttributeProcessor : OdinAttributeProcessor {
|
||||
public override bool CanProcessChildMemberAttributes(InspectorProperty parentProperty, MemberInfo member) {
|
||||
return member is FieldInfo;
|
||||
}
|
||||
|
||||
static object nullObject = new object();
|
||||
public override void ProcessChildMemberAttributes(InspectorProperty property, MemberInfo member, List<Attribute> attributes) {
|
||||
var field = member as FieldInfo;
|
||||
if (field?.DeclaringType == null) {
|
||||
return;
|
||||
}
|
||||
if (UnityFieldHelper.TryGetFieldAttributes(field, out var fieldAttributes)) {
|
||||
attributes.Clear();
|
||||
attributes.AddRange(fieldAttributes);
|
||||
}
|
||||
if (UnityFieldHelper.IsFieldHidden(field.DeclaringType, field.Name)) {
|
||||
attributes.Add(new HideIfAttribute("@true"));
|
||||
}
|
||||
// we assume this is always not null. Most of the times it will not be. If it is the side effect is some memory footprint which hopefully gets cleared when enough objects
|
||||
var key = property.ParentValues.FirstOrDefault() ?? nullObject;
|
||||
UnityFieldHelper.CacheFieldInvalidation(key, field, property.RefreshSetup);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c63452dd912fe4c46909c1c5ce844e69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,101 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
public enum PopupSource {
|
||||
Window,
|
||||
Overlay,
|
||||
}
|
||||
public class HotReloadEventPopup : PopupWindowContent {
|
||||
public static HotReloadEventPopup I = new HotReloadEventPopup();
|
||||
private Vector2 _PopupScrollPos;
|
||||
public bool open { get; private set; }
|
||||
private PopupSource source;
|
||||
private HotReloadRunTabState currentState;
|
||||
|
||||
public static void Open(PopupSource source, Vector2 pos) {
|
||||
I.source = source;
|
||||
PopupWindow.Show(new Rect(pos.x, pos.y, 0, 0), I);
|
||||
}
|
||||
|
||||
public override Vector2 GetWindowSize() {
|
||||
if (HotReloadRunTab.ShouldRenderConsumption(currentState)
|
||||
&& (HotReloadWindowStyles.windowScreenWidth <= Constants.ConsumptionsHideWidth
|
||||
|| HotReloadWindowStyles.windowScreenHeight <= Constants.ConsumptionsHideHeight
|
||||
|| source == PopupSource.Overlay)
|
||||
) {
|
||||
return new Vector2(600, 450);
|
||||
} else {
|
||||
return new Vector2(500, 375);
|
||||
}
|
||||
}
|
||||
|
||||
public void Repaint() {
|
||||
if (open) {
|
||||
PopupWindow.GetWindow<PopupWindow>().Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect) {
|
||||
if (Event.current.type == EventType.Layout) {
|
||||
currentState = HotReloadRunTabState.Current;
|
||||
}
|
||||
if (HotReloadWindowStyles.windowScreenWidth <= Constants.UpgradeLicenseNoteHideWidth
|
||||
|| HotReloadWindowStyles.windowScreenHeight <= Constants.UpgradeLicenseNoteHideHeight
|
||||
|| source == PopupSource.Overlay
|
||||
) {
|
||||
HotReloadRunTab.RenderUpgradeLicenseNote(currentState, HotReloadWindowStyles.UpgradeLicenseButtonOverlayStyle);
|
||||
}
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.helpBox)) {
|
||||
using (var scope = new EditorGUILayout.ScrollViewScope(_PopupScrollPos, GUIStyle.none, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(495))) {
|
||||
_PopupScrollPos.x = scope.scrollPosition.x;
|
||||
_PopupScrollPos.y = scope.scrollPosition.y;
|
||||
|
||||
|
||||
var renderDebuggerInfo = Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled;
|
||||
if ((HotReloadWindowStyles.windowScreenWidth <= Constants.ConsumptionsHideWidth
|
||||
|| HotReloadWindowStyles.windowScreenHeight <= Constants.ConsumptionsHideHeight
|
||||
|| source == PopupSource.Overlay) && !renderDebuggerInfo
|
||||
) {
|
||||
HotReloadRunTab.RenderLicenseInfo(currentState);
|
||||
}
|
||||
if (renderDebuggerInfo) {
|
||||
HotReloadRunTab.RenderDebuggerAttachedInfo(true);
|
||||
}
|
||||
|
||||
HotReloadRunTab.RenderBars(currentState);
|
||||
}
|
||||
}
|
||||
|
||||
bool rateAppShown = HotReloadWindow.ShouldShowRateApp();
|
||||
if ((HotReloadWindowStyles.windowScreenWidth <= Constants.RateAppHideWidth
|
||||
|| HotReloadWindowStyles.windowScreenHeight <= Constants.RateAppHideHeight
|
||||
|| source == PopupSource.Overlay)
|
||||
&& rateAppShown
|
||||
) {
|
||||
HotReloadWindow.RenderRateApp();
|
||||
}
|
||||
|
||||
if (HotReloadWindowStyles.windowScreenWidth <= Constants.EventFiltersShownHideWidth
|
||||
|| source == PopupSource.Overlay
|
||||
) {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
GUILayout.Space(21);
|
||||
HotReloadTimelineHelper.RenderAlertFilters();
|
||||
}
|
||||
}
|
||||
HotReloadState.ShowingRedDot = false;
|
||||
}
|
||||
|
||||
public override void OnOpen() {
|
||||
open = true;
|
||||
}
|
||||
|
||||
public override void OnClose() {
|
||||
_PopupScrollPos = Vector2.zero;
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00ec214cde074cf298acef73bb09a4fc
|
||||
timeCreated: 1696574416
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,179 @@
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using UnityEditor.Overlays;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Toolbars;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
[Overlay(typeof(SceneView), Translations.MenuItems.OverlayDescription, true)]
|
||||
[Icon("Assets/HotReload/Editor/Resources/Icon_DarkMode.png")]
|
||||
internal class HotReloadOverlay : ToolbarOverlay {
|
||||
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();
|
||||
tooltip = EditorIndicationState.IndicationStatusText;
|
||||
clicked += OnClick;
|
||||
EditorApplication.update += Update;
|
||||
}
|
||||
|
||||
void OnClick() {
|
||||
EditorWindow.GetWindow<HotReloadWindow>().Show();
|
||||
EditorWindow.GetWindow<HotReloadWindow>().SelectTab(typeof(HotReloadRunTab));
|
||||
}
|
||||
|
||||
void Update() {
|
||||
if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
|
||||
icon = GetIndicationIcon();
|
||||
tooltip = EditorIndicationState.IndicationStatusText;
|
||||
lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
|
||||
}
|
||||
}
|
||||
|
||||
~HotReloadToolbarIndicationButton() {
|
||||
clicked -= OnClick;
|
||||
EditorApplication.update -= Update;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorToolbarElement(id, typeof(SceneView))]
|
||||
class HotReloadToolbarEventsButton : EditorToolbarButton, IAccessContainerWindow {
|
||||
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 = Translations.Timeline.EventsTooltip;
|
||||
clicked += OnClick;
|
||||
EditorApplication.update += Update;
|
||||
}
|
||||
|
||||
void OnClick() {
|
||||
HotReloadEventPopup.Open(PopupSource.Overlay, Event.current.mousePosition);
|
||||
}
|
||||
|
||||
void Update() {
|
||||
if (lastShowingRedDot != HotReloadState.ShowingRedDot) {
|
||||
icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
|
||||
lastShowingRedDot = HotReloadState.ShowingRedDot;
|
||||
}
|
||||
}
|
||||
|
||||
~HotReloadToolbarEventsButton() {
|
||||
clicked -= OnClick;
|
||||
EditorApplication.update -= Update;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[EditorToolbarElement(id, typeof(SceneView))]
|
||||
class HotReloadToolbarRecompileButton : EditorToolbarButton, IAccessContainerWindow {
|
||||
internal const string id = "HotReloadOverlay/RecompileButton";
|
||||
|
||||
public EditorWindow containerWindow { get; set; }
|
||||
|
||||
private Texture2D refreshIcon => GUIHelper.GetInvertibleIcon(InvertibleIcon.Recompile);
|
||||
internal HotReloadToolbarRecompileButton() {
|
||||
icon = refreshIcon;
|
||||
tooltip = Translations.Miscellaneous.OverlayTooltipRecompile;
|
||||
clicked += HotReloadRunTab.RecompileWithChecks;
|
||||
}
|
||||
}
|
||||
|
||||
private static Texture2D latestIcon;
|
||||
private static Dictionary<string, Texture2D> iconTextures = new Dictionary<string, Texture2D>();
|
||||
private static Spinner spinner = new Spinner(100);
|
||||
private static Texture2D GetIndicationIcon() {
|
||||
if (EditorIndicationState.IndicationIconPath == null || EditorIndicationState.SpinnerActive) {
|
||||
latestIcon = spinner.GetIcon();
|
||||
} else {
|
||||
latestIcon = GUIHelper.GetLocalIcon(EditorIndicationState.IndicationIconPath);
|
||||
}
|
||||
return latestIcon;
|
||||
}
|
||||
|
||||
private static Image indicationIcon;
|
||||
private static Label indicationText;
|
||||
|
||||
bool initialized;
|
||||
/// <summary>
|
||||
/// Create Hot Reload overlay panel.
|
||||
/// </summary>
|
||||
public override VisualElement CreatePanelContent() {
|
||||
var root = new VisualElement() { name = Translations.UI.OverlayPanelName };
|
||||
root.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
indicationIcon = new Image() { image = GUIHelper.GetLocalIcon(EditorIndicationState.greyIconPath) };
|
||||
indicationIcon.style.height = 30;
|
||||
indicationIcon.style.width = 30;
|
||||
indicationIcon.style.marginLeft = 2;
|
||||
indicationIcon.style.marginTop = 1;
|
||||
indicationIcon.style.marginRight = 5;
|
||||
|
||||
indicationText = new Label(){text = EditorIndicationState.IndicationStatusText};
|
||||
indicationText.style.paddingTop = 9;
|
||||
indicationText.style.marginLeft = new StyleLength(StyleKeyword.Auto);
|
||||
indicationText.style.marginRight = new StyleLength(StyleKeyword.Auto);
|
||||
|
||||
root.Add(indicationIcon);
|
||||
root.Add(indicationText);
|
||||
root.style.width = 190;
|
||||
root.style.height = 32;
|
||||
initialized = true;
|
||||
return root;
|
||||
}
|
||||
|
||||
static bool _repaint;
|
||||
static bool _instantRepaint;
|
||||
static DateTime _lastRepaint;
|
||||
private void Update() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
|
||||
indicationIcon.image = GetIndicationIcon();
|
||||
indicationText.text = EditorIndicationState.IndicationStatusText;
|
||||
lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
|
||||
}
|
||||
try {
|
||||
if (HotReloadEventPopup.I.open
|
||||
&& EditorWindow.mouseOverWindow
|
||||
&& EditorWindow.mouseOverWindow?.GetType() == typeof(UnityEditor.PopupWindow)
|
||||
) {
|
||||
_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;
|
||||
}
|
||||
if (_instantRepaint) {
|
||||
HotReloadEventPopup.I.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
~HotReloadOverlay() {
|
||||
EditorApplication.update -= Update;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91650b4b0d054bdf9c1e922305e6a61a
|
||||
timeCreated: 1685130321
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,524 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using RuntimeLocalization = SingularityGroup.HotReload.Localization;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class HotReloadPrefs {
|
||||
private const string RemoteServerKey = "HotReloadWindow.RemoteServer";
|
||||
private const string RemoteServerHostKey = "HotReloadWindow.RemoteServerHost";
|
||||
private const string LicenseEmailKey = "HotReloadWindow.LicenseEmail";
|
||||
private const string RenderAuthLoginKey = "HotReloadWindow.RenderAuthLogin";
|
||||
private const string FirstLoginCachedKey = "HotReloadWindow.FirstLoginCachedKey";
|
||||
[Obsolete]
|
||||
private const string ShowOnStartupKey = "HotReloadWindow.ShowOnStartup";
|
||||
private const string PasswordCachedKey = "HotReloadWindow.PasswordCached";
|
||||
private const string ExposeServerToLocalNetworkKey = "HotReloadWindow.ExposeServerToLocalNetwork";
|
||||
private const string ErrorHiddenCachedKey = "HotReloadWindow.ErrorHiddenCachedKey";
|
||||
private const string RefreshManuallyTipCachedKey = "HotReloadWindow.RefreshManuallyTipCachedKey";
|
||||
private const string ShowLoginCachedKey = "HotReloadWindow.ShowLoginCachedKey";
|
||||
private const string ConfigurationKey = "HotReloadWindow.Configuration";
|
||||
private const string AvdancedKey = "HotReloadWindow.Avdanced";
|
||||
private const string ShowPromoCodesCachedKey = "HotReloadWindow.ShowPromoCodesCached";
|
||||
private const string ShowOnDeviceKey = "HotReloadWindow.ShowOnDevice";
|
||||
private const string ShowChangelogKey = "HotReloadWindow.ShowChangelog";
|
||||
private const string UnsupportedChangesKey = "HotReloadWindow.ShowUnsupportedChanges";
|
||||
private const string LoggedBurstHintKey = "HotReloadWindow.LoggedBurstHint";
|
||||
private const string ShouldDoAutoRefreshFixupKey = "HotReloadWindow.ShouldDoAutoRefreshFixup";
|
||||
private const string ActiveDaysKey = "HotReloadWindow.ActiveDays";
|
||||
[Obsolete]
|
||||
private const string RateAppShownKey = "HotReloadWindow.RateAppShown";
|
||||
private const string PatchesCollapseKey = "HotReloadWindow.PatchesCollapse";
|
||||
private const string PatchesGroupAllKey = "HotReloadWindow.PatchesGroupAll";
|
||||
private const string LaunchOnEditorStartKey = "HotReloadWindow.LaunchOnEditorStart";
|
||||
private const string AutoClearTimelineKey = "HotReloadWindow.AutoClearTimeline";
|
||||
private const string AutoRecompileUnsupportedChangesKey = "HotReloadWindow.AutoRecompileUnsupportedChanges";
|
||||
private const string AutoRecompilePartiallyUnsupportedChangesKey = "HotReloadWindow.AutoRecompilePartiallyUnsupportedChanges";
|
||||
private const string DisplayNewMonobehaviourMethodsAsPartiallySupportedKey = "HotReloadWindow.DisplayNewMonobehaviourMethodsAsPartiallySupported";
|
||||
private const string ShowNotificationsKey = "HotReloadWindow.ShowNotifications";
|
||||
private const string ShowPatchingNotificationsKey = "HotReloadWindow.ShowPatchingNotifications";
|
||||
private const string ShowCompilingUnsupportedNotificationsKey = "HotReloadWindow.ShowCompilingUnsupportedNotifications";
|
||||
private const string AutoRecompileUnsupportedChangesImmediatelyKey = "HotReloadWindow.AutoRecompileUnsupportedChangesImmediately";
|
||||
private const string AutoRecompileUnsupportedChangesOnExitPlayModeKey = "HotReloadWindow.AutoRecompileUnsupportedChangesOnExitPlayMode";
|
||||
private const string AutoRecompileUnsupportedChangesInPlayModeKey = "HotReloadWindow.AutoRecompileUnsupportedChangesInPlayMode";
|
||||
private const string AutoRecompileUnsupportedChangesInEditModeKey = "HotReloadWindow.AutoRecompileUnsupportedChangesInEditMode";
|
||||
private const string AutoRecompileInspectorFieldsEditKey = "HotReloadWindow.AutoRecompileInspectorFieldsEdit";
|
||||
private const string AllowDisableUnityAutoRefreshKey = "HotReloadWindow.AllowDisableUnityAutoRefresh";
|
||||
private const string DefaultAutoRefreshKey = "HotReloadWindow.DefaultAutoRefresh";
|
||||
private const string DefaultAutoRefreshModeKey = "HotReloadWindow.DefaultAutoRefreshMode";
|
||||
private const string DefaultScriptCompilationKeyKey = "HotReloadWindow.DefaultScriptCompilationKey";
|
||||
private const string DefaultEditorTintKey = "HotReloadWindow.DefaultEditorTint";
|
||||
private const string AppliedAutoRefreshKey = "HotReloadWindow.AppliedAutoRefresh";
|
||||
private const string AppliedScriptCompilationKey = "HotReloadWindow.AppliedScriptCompilation";
|
||||
private const string AppliedEditorTintKey = "HotReloadWindow.AppliedEditorTint";
|
||||
private const string AllAssetChangesKey = "HotReloadWindow.AllAssetChanges";
|
||||
private const string IncludeShaderChangesKey = "HotReloadWindow.IncludeShaderChanges";
|
||||
private const string DisableConsoleWindowKey = "HotReloadWindow.DisableConsoleWindow";
|
||||
private const string DisableDetailedErrorReportingKey = "HotReloadWindow.DisableDetailedErrorReporting";
|
||||
private const string DebuggerCompatibilityEnabledKey = "HotReloadWindow.DebuggerCompatibilityEnabled";
|
||||
private const string PauseHotReloadInEditModeKey = "HotReloadWindow.PauseHotReloadInEditMode";
|
||||
private const string RedeemLicenseEmailKey = "HotReloadWindow.RedeemLicenseEmail";
|
||||
private const string RedeemLicenseInvoiceKey = "HotReloadWindow.RedeemLicenseInvoice";
|
||||
private const string RunTabEventsSuggestionsFoldoutKey = "HotReloadWindow.RunTabEventsSuggestionsFoldout";
|
||||
private const string RunTabEventsTimelineFoldoutKey = "HotReloadWindow.RunTabEventsTimelineFoldout";
|
||||
private const string RunTabUnsupportedChangesFilterKey = "HotReloadWindow.RunTabUnsupportedChangesFilter";
|
||||
private const string RunTabCompileErrorFilterKey = "HotReloadWindow.RunTabCompileErrorFilter";
|
||||
private const string RunTabPartiallyAppliedPatchesFilterKey = "HotReloadWindow.RunTabPartiallyAppliedPatchesFilter";
|
||||
private const string RunTabUndetectedPatchesFilterKey = "HotReloadWindow.RunTabUndetectedPatchesFilter";
|
||||
private const string RunTabAppliedPatchesFilterKey = "HotReloadWindow.RunTabAppliedPatchesFilter";
|
||||
private const string RecompileDialogueShownKey = "HotReloadWindow.RecompileDialogueShown";
|
||||
private const string ApplyFieldInitiailzerEditsToExistingClassInstancesKey = "HotReloadWindow.ApplyFieldInitiailzerEditsToExistingClassInstances";
|
||||
private const string LoggedInlinedMethodsDialogueKey = "HotReloadWindow.LoggedInlinedMethodsDialogue";
|
||||
private const string OpenedWindowAtLeastOnceKey = "HotReloadWindow.OpenedWindowAtLeastOnce";
|
||||
private const string DeactivateHotReloadKey = "HotReloadWindow.DeactivateHotReload";
|
||||
private const string ActiveLocaleKey = "HotReloadWindow.ActiveLocale";
|
||||
public const string DontShowPromptForDownloadKey = "ServerDownloader.DontShowPromptForDownload";
|
||||
public const string DebuggerOnboardingShownKey = "HotReloadWindow.DebuggerOnboardingShownKey";
|
||||
#if UNITY_EDITOR_WIN
|
||||
public const string UseWatchmanKey = "ServerDownloader.UseWatchman";
|
||||
#endif
|
||||
|
||||
[Obsolete] public const string AllowHttpSettingCacheKey = "HotReloadWindow.AllowHttpSettingCacheKey";
|
||||
[Obsolete] public const string AutoRefreshSettingCacheKey = "HotReloadWindow.AutoRefreshSettingCacheKey";
|
||||
[Obsolete] public const string ScriptCompilationSettingCacheKey = "HotReloadWindow.ScriptCompilationSettingCacheKey";
|
||||
[Obsolete] public const string ProjectGenerationSettingCacheKey = "HotReloadWindow.ProjectGenerationSettingCacheKey";
|
||||
|
||||
|
||||
[Obsolete]
|
||||
public static bool RemoteServer {
|
||||
get { return EditorPrefs.GetBool(RemoteServerKey, false); }
|
||||
set { EditorPrefs.SetBool(RemoteServerKey, value); }
|
||||
}
|
||||
|
||||
public static bool DontShowPromptForDownload {
|
||||
get { return EditorPrefs.GetBool(DontShowPromptForDownloadKey, false); }
|
||||
set { EditorPrefs.SetBool(DontShowPromptForDownloadKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static string RemoteServerHost {
|
||||
get { return EditorPrefs.GetString(RemoteServerHostKey); }
|
||||
set { EditorPrefs.SetString(RemoteServerHostKey, value); }
|
||||
}
|
||||
|
||||
public static string LicenseEmail {
|
||||
get { return EditorPrefs.GetString(LicenseEmailKey); }
|
||||
set { EditorPrefs.SetString(LicenseEmailKey, value); }
|
||||
}
|
||||
|
||||
public static string LicensePassword {
|
||||
get { return EditorPrefs.GetString(PasswordCachedKey); }
|
||||
set { EditorPrefs.SetString(PasswordCachedKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool RenderAuthLogin { // false = render free trial
|
||||
get { return EditorPrefs.GetBool(RenderAuthLoginKey); }
|
||||
set { EditorPrefs.SetBool(RenderAuthLoginKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool FirstLogin {
|
||||
get { return EditorPrefs.GetBool(FirstLoginCachedKey, true); }
|
||||
set { EditorPrefs.SetBool(FirstLoginCachedKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static string ShowOnStartupLegacy { // WindowAutoOpen
|
||||
get { return EditorPrefs.GetString(ShowOnStartupKey); }
|
||||
set { EditorPrefs.SetString(ShowOnStartupKey, value); }
|
||||
}
|
||||
|
||||
public static string showOnStartupPath { get; }= Path.Combine(CliUtils.GetAppDataPath(), "showOnStartup.txt");
|
||||
static ShowOnStartupEnum? showOnStartup;
|
||||
public static ShowOnStartupEnum ShowOnStartup {
|
||||
get {
|
||||
if (showOnStartup != null) {
|
||||
return showOnStartup.Value;
|
||||
}
|
||||
if (!File.Exists(showOnStartupPath)) {
|
||||
showOnStartup = ShowOnStartupEnum.Always;
|
||||
return showOnStartup.Value;
|
||||
}
|
||||
var text = File.ReadAllText(showOnStartupPath);
|
||||
ShowOnStartupEnum _showOnStartup;
|
||||
if (Enum.TryParse(text, true, out _showOnStartup)) {
|
||||
showOnStartup = _showOnStartup;
|
||||
return showOnStartup.Value;
|
||||
}
|
||||
showOnStartup = ShowOnStartupEnum.Always;
|
||||
return showOnStartup.Value;
|
||||
}
|
||||
set {
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(showOnStartupPath));
|
||||
File.WriteAllText(showOnStartupPath, value.ToString());
|
||||
showOnStartup = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool ErrorHidden {
|
||||
get { return EditorPrefs.GetBool(ErrorHiddenCachedKey); }
|
||||
set { EditorPrefs.SetBool(ErrorHiddenCachedKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowLogin {
|
||||
get { return EditorPrefs.GetBool(ShowLoginCachedKey, true); }
|
||||
set { EditorPrefs.SetBool(ShowLoginCachedKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowConfiguration {
|
||||
get { return EditorPrefs.GetBool(ConfigurationKey, true); }
|
||||
set { EditorPrefs.SetBool(ConfigurationKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowAdvanced {
|
||||
get { return EditorPrefs.GetBool(AvdancedKey, false); }
|
||||
set { EditorPrefs.SetBool(AvdancedKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowPromoCodes {
|
||||
get { return EditorPrefs.GetBool(ShowPromoCodesCachedKey, true); }
|
||||
set { EditorPrefs.SetBool(ShowPromoCodesCachedKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowOnDevice {
|
||||
get { return EditorPrefs.GetBool(ShowOnDeviceKey, true); }
|
||||
set { EditorPrefs.SetBool(ShowOnDeviceKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowChangeLog {
|
||||
get { return EditorPrefs.GetBool(ShowChangelogKey, true); }
|
||||
set { EditorPrefs.SetBool(ShowChangelogKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowUnsupportedChanges {
|
||||
get { return EditorPrefs.GetBool(UnsupportedChangesKey, true); }
|
||||
set { EditorPrefs.SetBool(UnsupportedChangesKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool RefreshManuallyTip {
|
||||
get { return EditorPrefs.GetBool(RefreshManuallyTipCachedKey); }
|
||||
set { EditorPrefs.SetBool(RefreshManuallyTipCachedKey, value); }
|
||||
}
|
||||
|
||||
public static bool LoggedBurstHint {
|
||||
get { return EditorPrefs.GetBool(LoggedBurstHintKey); }
|
||||
set { EditorPrefs.SetBool(LoggedBurstHintKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool ShouldDoAutoRefreshFixup {
|
||||
get { return EditorPrefs.GetBool(ShouldDoAutoRefreshFixupKey, true); }
|
||||
set { EditorPrefs.SetBool(ShouldDoAutoRefreshFixupKey, value); }
|
||||
}
|
||||
|
||||
public static string ActiveDays {
|
||||
get { return EditorPrefs.GetString(ActiveDaysKey, string.Empty); }
|
||||
set { EditorPrefs.SetString(ActiveDaysKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool RateAppShownLegacy {
|
||||
get { return EditorPrefs.GetBool(RateAppShownKey, false); }
|
||||
set { EditorPrefs.SetBool(RateAppShownKey, value); }
|
||||
}
|
||||
|
||||
static string rateAppPath = Path.Combine(CliUtils.GetAppDataPath(), "ratedApp.txt");
|
||||
static bool? rateAppShown;
|
||||
public static bool RateAppShown {
|
||||
get {
|
||||
if (rateAppShown != null) {
|
||||
return rateAppShown.Value;
|
||||
}
|
||||
rateAppShown = File.Exists(rateAppPath);
|
||||
return rateAppShown.Value;
|
||||
}
|
||||
set {
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(rateAppPath));
|
||||
if (value && !File.Exists(rateAppPath)) {
|
||||
using (File.Create(rateAppPath)) { }
|
||||
} else if (!value && File.Exists(rateAppPath)) {
|
||||
File.Delete(rateAppPath);
|
||||
}
|
||||
rateAppShown = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool PatchesGroupAll {
|
||||
get { return EditorPrefs.GetBool(PatchesGroupAllKey, false); }
|
||||
set { EditorPrefs.SetBool(PatchesGroupAllKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static bool PatchesCollapse {
|
||||
get { return EditorPrefs.GetBool(PatchesCollapseKey, true); }
|
||||
set { EditorPrefs.SetBool(PatchesCollapseKey, value); }
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public static ShowOnStartupEnum GetShowOnStartupEnum() {
|
||||
ShowOnStartupEnum showOnStartupEnum;
|
||||
if (Enum.TryParse(HotReloadPrefs.ShowOnStartupLegacy, true, out showOnStartupEnum)) {
|
||||
return showOnStartupEnum;
|
||||
}
|
||||
return ShowOnStartupEnum.Always;
|
||||
}
|
||||
|
||||
public static bool ExposeServerToLocalNetwork {
|
||||
get { return EditorPrefs.GetBool(ExposeServerToLocalNetworkKey, false); }
|
||||
set { EditorPrefs.SetBool(ExposeServerToLocalNetworkKey, value); }
|
||||
}
|
||||
|
||||
public static bool LaunchOnEditorStart {
|
||||
get { return EditorPrefs.GetBool(LaunchOnEditorStartKey, false); }
|
||||
set { EditorPrefs.SetBool(LaunchOnEditorStartKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoClearTimeline {
|
||||
get { return EditorPrefs.GetBool(AutoClearTimelineKey, true); }
|
||||
set { EditorPrefs.SetBool(AutoClearTimelineKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileUnsupportedChanges {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileUnsupportedChangesKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileUnsupportedChangesKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompilePartiallyUnsupportedChanges {
|
||||
get { return EditorPrefs.GetBool(AutoRecompilePartiallyUnsupportedChangesKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompilePartiallyUnsupportedChangesKey, value); }
|
||||
}
|
||||
|
||||
public static bool DisplayNewMonobehaviourMethodsAsPartiallySupported {
|
||||
get { return EditorPrefs.GetBool(DisplayNewMonobehaviourMethodsAsPartiallySupportedKey, false); }
|
||||
set { EditorPrefs.SetBool(DisplayNewMonobehaviourMethodsAsPartiallySupportedKey, 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); }
|
||||
}
|
||||
|
||||
public static bool ShowCompilingUnsupportedNotifications {
|
||||
get { return EditorPrefs.GetBool(ShowCompilingUnsupportedNotificationsKey, true); }
|
||||
set { EditorPrefs.SetBool(ShowCompilingUnsupportedNotificationsKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileUnsupportedChangesImmediately {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileUnsupportedChangesImmediatelyKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileUnsupportedChangesImmediatelyKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileUnsupportedChangesOnExitPlayMode {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileUnsupportedChangesOnExitPlayModeKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileUnsupportedChangesOnExitPlayModeKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileUnsupportedChangesInPlayMode {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileUnsupportedChangesInPlayModeKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileUnsupportedChangesInPlayModeKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileInspectorFieldsEdit {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileInspectorFieldsEditKey, false); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileInspectorFieldsEditKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoRecompileUnsupportedChangesInEditMode {
|
||||
get { return EditorPrefs.GetBool(AutoRecompileUnsupportedChangesInEditModeKey, true); }
|
||||
set { EditorPrefs.SetBool(AutoRecompileUnsupportedChangesInEditModeKey, value); }
|
||||
}
|
||||
|
||||
public static bool AllowDisableUnityAutoRefresh {
|
||||
get { return EditorPrefs.GetBool(AllowDisableUnityAutoRefreshKey, false); }
|
||||
set { EditorPrefs.SetBool(AllowDisableUnityAutoRefreshKey, value); }
|
||||
}
|
||||
|
||||
public static int DefaultAutoRefresh {
|
||||
get { return EditorPrefs.GetInt(DefaultAutoRefreshKey, -1); }
|
||||
set { EditorPrefs.SetInt(DefaultAutoRefreshKey, value); }
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public static int DefaultAutoRefreshMode {
|
||||
get { return EditorPrefs.GetInt(DefaultAutoRefreshModeKey, -1); }
|
||||
set { EditorPrefs.SetInt(DefaultAutoRefreshModeKey, value); }
|
||||
}
|
||||
|
||||
public static int DefaultScriptCompilation {
|
||||
get { return EditorPrefs.GetInt(DefaultScriptCompilationKeyKey, -1); }
|
||||
set { EditorPrefs.SetInt(DefaultScriptCompilationKeyKey, value); }
|
||||
}
|
||||
|
||||
public static Color? DefaultEditorTint {
|
||||
get { return ColorFromString(EditorPrefs.GetString(DefaultEditorTintKey, string.Empty)); }
|
||||
set { EditorPrefs.SetString(DefaultEditorTintKey, ColorToString(value)); }
|
||||
}
|
||||
|
||||
public static bool AppliedAutoRefresh {
|
||||
get { return EditorPrefs.GetBool(AppliedAutoRefreshKey); }
|
||||
set { EditorPrefs.SetBool(AppliedAutoRefreshKey, value); }
|
||||
}
|
||||
|
||||
public static bool AppliedScriptCompilation {
|
||||
get { return EditorPrefs.GetBool(AppliedScriptCompilationKey); }
|
||||
set { EditorPrefs.SetBool(AppliedScriptCompilationKey, value); }
|
||||
}
|
||||
|
||||
public static Color? AppliedEditorTint {
|
||||
get { return ColorFromString(EditorPrefs.GetString(AppliedEditorTintKey, string.Empty)); }
|
||||
set { EditorPrefs.SetString(AppliedEditorTintKey, ColorToString(value)); }
|
||||
}
|
||||
|
||||
public static bool AllAssetChanges {
|
||||
get { return EditorPrefs.GetBool(AllAssetChangesKey, false); }
|
||||
set { EditorPrefs.SetBool(AllAssetChangesKey, value); }
|
||||
}
|
||||
|
||||
public static bool IncludeShaderChanges {
|
||||
get { return EditorPrefs.GetBool(IncludeShaderChangesKey, false); }
|
||||
set { EditorPrefs.SetBool(IncludeShaderChangesKey, value); }
|
||||
}
|
||||
|
||||
public static bool DisableConsoleWindow {
|
||||
get { return EditorPrefs.GetBool(DisableConsoleWindowKey, false); }
|
||||
set { EditorPrefs.SetBool(DisableConsoleWindowKey, value); }
|
||||
}
|
||||
|
||||
public static string RedeemLicenseEmail {
|
||||
get { return EditorPrefs.GetString(RedeemLicenseEmailKey); }
|
||||
set { EditorPrefs.SetString(RedeemLicenseEmailKey, value); }
|
||||
}
|
||||
|
||||
public static string RedeemLicenseInvoice {
|
||||
get { return EditorPrefs.GetString(RedeemLicenseInvoiceKey); }
|
||||
set { EditorPrefs.SetString(RedeemLicenseInvoiceKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabEventsTimelineFoldout {
|
||||
get { return EditorPrefs.GetBool(RunTabEventsTimelineFoldoutKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabEventsTimelineFoldoutKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabEventsSuggestionsFoldout {
|
||||
get { return EditorPrefs.GetBool(RunTabEventsSuggestionsFoldoutKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabEventsSuggestionsFoldoutKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabUnsupportedChangesFilter {
|
||||
get { return EditorPrefs.GetBool(RunTabUnsupportedChangesFilterKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabUnsupportedChangesFilterKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabCompileErrorFilter {
|
||||
get { return EditorPrefs.GetBool(RunTabCompileErrorFilterKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabCompileErrorFilterKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabPartiallyAppliedPatchesFilter {
|
||||
get { return EditorPrefs.GetBool(RunTabPartiallyAppliedPatchesFilterKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabPartiallyAppliedPatchesFilterKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabUndetectedPatchesFilter {
|
||||
get { return EditorPrefs.GetBool(RunTabUndetectedPatchesFilterKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabUndetectedPatchesFilterKey, value); }
|
||||
}
|
||||
|
||||
public static bool RunTabAppliedPatchesFilter {
|
||||
get { return EditorPrefs.GetBool(RunTabAppliedPatchesFilterKey, true); }
|
||||
set { EditorPrefs.SetBool(RunTabAppliedPatchesFilterKey, value); }
|
||||
}
|
||||
|
||||
public static bool RecompileDialogueShown {
|
||||
get { return EditorPrefs.GetBool(RecompileDialogueShownKey); }
|
||||
set { EditorPrefs.SetBool(RecompileDialogueShownKey, value); }
|
||||
}
|
||||
|
||||
public static bool OpenedWindowAtLeastOnce {
|
||||
get { return EditorPrefs.GetBool(OpenedWindowAtLeastOnceKey); }
|
||||
set { EditorPrefs.SetBool(OpenedWindowAtLeastOnceKey, value); }
|
||||
}
|
||||
|
||||
private const string rgbaDelimiter = ";";
|
||||
public static string ColorToString(Color? _color) {
|
||||
if (_color == null) {
|
||||
return null;
|
||||
}
|
||||
var color = _color.Value;
|
||||
var cultInfo = CultureInfo.InvariantCulture;
|
||||
string[] rgbaList = { color.r.ToString(cultInfo), color.g.ToString(cultInfo), color.b.ToString(cultInfo), color.a.ToString(cultInfo)};
|
||||
return String.Join(rgbaDelimiter, rgbaList);
|
||||
}
|
||||
|
||||
public static Color? ColorFromString(string ser) {
|
||||
if (string.IsNullOrEmpty(ser)) {
|
||||
return null;
|
||||
}
|
||||
string[] rgbaParts = ser.Split(rgbaDelimiter.ToCharArray());
|
||||
return new Color(float.Parse(rgbaParts[0]), float.Parse(rgbaParts[1]),float.Parse(rgbaParts[2]),float.Parse(rgbaParts[3]));
|
||||
}
|
||||
|
||||
[Obsolete("was not implemented")]
|
||||
public static bool ApplyFieldInitiailzerEditsToExistingClassInstances {
|
||||
get { return EditorPrefs.GetBool(ApplyFieldInitiailzerEditsToExistingClassInstancesKey); }
|
||||
set { EditorPrefs.SetBool(ApplyFieldInitiailzerEditsToExistingClassInstancesKey, value); }
|
||||
}
|
||||
|
||||
public static bool LoggedInlinedMethodsDialogue {
|
||||
get { return EditorPrefs.GetBool(LoggedInlinedMethodsDialogueKey); }
|
||||
set { EditorPrefs.SetBool(LoggedInlinedMethodsDialogueKey, value); }
|
||||
}
|
||||
|
||||
public static bool DeactivateHotReload {
|
||||
get { return EditorPrefs.GetBool(DeactivateHotReloadKey); }
|
||||
set { EditorPrefs.SetBool(DeactivateHotReloadKey, value); }
|
||||
}
|
||||
|
||||
public static bool DisableDetailedErrorReporting {
|
||||
get { return EditorPrefs.GetBool(DisableDetailedErrorReportingKey, false); }
|
||||
set { EditorPrefs.SetBool(DisableDetailedErrorReportingKey, value); }
|
||||
}
|
||||
|
||||
public static bool PauseHotReloadInEditMode {
|
||||
get { return EditorPrefs.GetBool(PauseHotReloadInEditModeKey, false); }
|
||||
set { EditorPrefs.SetBool(PauseHotReloadInEditModeKey, value); }
|
||||
}
|
||||
|
||||
public static bool AutoDisableHotReloadWithDebugger {
|
||||
get { return EditorPrefs.GetBool(DebuggerCompatibilityEnabledKey, true); }
|
||||
set { EditorPrefs.SetBool(DebuggerCompatibilityEnabledKey, value); }
|
||||
}
|
||||
|
||||
public static string ActiveLocale {
|
||||
get { return EditorPrefs.GetString(ActiveLocaleKey, PackageConst.DefaultLocale); }
|
||||
set { EditorPrefs.SetString(ActiveLocaleKey, value); }
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR_WIN
|
||||
public static bool UseWatchman {
|
||||
get { return EditorPrefs.GetBool(UseWatchmanKey, PackageConst.DefaultLocale == RuntimeLocalization.Locale.English); }
|
||||
set { EditorPrefs.SetBool(UseWatchmanKey, value); }
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool DebuggerOnboardingShown {
|
||||
get { return EditorPrefs.GetBool(DebuggerOnboardingShownKey); }
|
||||
set { EditorPrefs.SetBool(DebuggerOnboardingShownKey, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96451431b50143944b85d4fbdde5f104
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
static class HotReloadSettingsEditor {
|
||||
/// Ensure settings asset file is created and saved
|
||||
public static void EnsureSettingsCreated(HotReloadSettingsObject asset) {
|
||||
if (!SettingsExists()) {
|
||||
CreateNewSettingsFile(asset, HotReloadSettingsObject.editorAssetPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load existing settings asset or return the default settings
|
||||
public static HotReloadSettingsObject LoadSettingsOrDefault() {
|
||||
if (SettingsExists()) {
|
||||
return AssetDatabase.LoadAssetAtPath<HotReloadSettingsObject>(HotReloadSettingsObject.editorAssetPath);
|
||||
} else {
|
||||
// create an instance with default values
|
||||
return ScriptableObject.CreateInstance<HotReloadSettingsObject>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create settings asset file
|
||||
/// </summary>
|
||||
/// <remarks>Assume that settings asset doesn't exist yet</remarks>
|
||||
/// <returns>The settings asset</returns>
|
||||
static void CreateNewSettingsFile(HotReloadSettingsObject asset, string editorAssetPath) {
|
||||
// create new settings asset
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(editorAssetPath));
|
||||
if (asset == null) {
|
||||
asset = ScriptableObject.CreateInstance<HotReloadSettingsObject>();
|
||||
}
|
||||
AssetDatabase.CreateAsset(asset, editorAssetPath);
|
||||
// Saving the asset isn't needed right after you created it. Unity will save it at the appropriate time.
|
||||
// Troy: I tested in Unity 2018 LTS, first Android build creates the asset file and asset is included in the build.
|
||||
}
|
||||
|
||||
#region include/exclude in build
|
||||
|
||||
private static bool SettingsExists() {
|
||||
return AssetExists(HotReloadSettingsObject.editorAssetPath);
|
||||
}
|
||||
|
||||
private static bool AssetExists(string assetPath) {
|
||||
return AssetDatabase.GetMainAssetTypeAtPath(assetPath) != null;
|
||||
}
|
||||
|
||||
public static void AddOrRemoveFromBuild(bool includeSettingsInBuild) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
AssetDatabase.StartAssetEditing();
|
||||
var so = LoadSettingsOrDefault();
|
||||
try {
|
||||
if (includeSettingsInBuild) {
|
||||
// Note: don't need to force create settings because we know the defaults in player.
|
||||
so.EnsurePrefabSetCorrectly();
|
||||
EnsureSettingsCreated(so);
|
||||
} else {
|
||||
// this block shouldn't create the asset file, but it's also fine if it does
|
||||
so.EnsurePrefabNotInBuild();
|
||||
}
|
||||
} finally {
|
||||
AssetDatabase.StopAssetEditing();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0f4231ca4f63e54da0ecf87ab62c381
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,92 @@
|
||||
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 ShowedFieldInitializerWithSideEffectsKey = "HotReloadWindow.ShowedFieldInitializerWithSideEffects";
|
||||
private const string ShowedAddMonobehaviourMethodsKey = "HotReloadWindow.ShowedAddMonobehaviourMethods";
|
||||
private const string ShowedFieldInitializerExistingInstancesEditedKey = "HotReloadWindow.ShowedFieldInitializerExistingInstancesEdited";
|
||||
private const string ShowedFieldInitializerExistingInstancesUneditedKey = "HotReloadWindow.ShowedFieldInitializerExistingInstancesUnedited";
|
||||
private const string RecompiledUnsupportedChangesOnExitPlaymodeKey = "HotReloadWindow.RecompiledUnsupportedChangesOnExitPlaymode";
|
||||
private const string RecompiledUnsupportedChangesInPlaymodeKey = "HotReloadWindow.RecompiledUnsupportedChangesInPlaymode";
|
||||
private const string EditorCodePatcherInitKey = "HotReloadWindow.EditorCodePatcherInit";
|
||||
private const string ShowedDebuggerCompatibilityKey = "HotReloadWindow.ShowedDebuggerCompatibility";
|
||||
private const string DisallowedAutoRefreshKey = "HotReloadWindow.DisallowedAutoRefresh";
|
||||
private const string WarnedDebuggerAttachedKey = "HotReloadWindow.WarnedDebuggerAttached";
|
||||
|
||||
|
||||
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); }
|
||||
set { SessionState.SetString(LastPatchIdKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowingRedDot {
|
||||
get { return SessionState.GetBool(ShowingRedDotKey, false); }
|
||||
set { SessionState.SetBool(ShowingRedDotKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedEditorsWithoutHR {
|
||||
get { return SessionState.GetBool(ShowedEditorsWithoutHRKey, false); }
|
||||
set { SessionState.SetBool(ShowedEditorsWithoutHRKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedFieldInitializerWithSideEffects {
|
||||
get { return SessionState.GetBool(ShowedFieldInitializerWithSideEffectsKey, false); }
|
||||
set { SessionState.SetBool(ShowedFieldInitializerWithSideEffectsKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedAddMonobehaviourMethods {
|
||||
get { return SessionState.GetBool(ShowedAddMonobehaviourMethodsKey, false); }
|
||||
set { SessionState.SetBool(ShowedAddMonobehaviourMethodsKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedFieldInitializerExistingInstancesEdited {
|
||||
get { return SessionState.GetBool(ShowedFieldInitializerExistingInstancesEditedKey, false); }
|
||||
set { SessionState.SetBool(ShowedFieldInitializerExistingInstancesEditedKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedFieldInitializerExistingInstancesUnedited {
|
||||
get { return SessionState.GetBool(ShowedFieldInitializerExistingInstancesUneditedKey, false); }
|
||||
set { SessionState.SetBool(ShowedFieldInitializerExistingInstancesUneditedKey, value); }
|
||||
}
|
||||
|
||||
public static bool RecompiledUnsupportedChangesOnExitPlaymode {
|
||||
get { return SessionState.GetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, false); }
|
||||
set { SessionState.SetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, value); }
|
||||
}
|
||||
|
||||
public static bool RecompiledUnsupportedChangesInPlaymode {
|
||||
get { return SessionState.GetBool(RecompiledUnsupportedChangesInPlaymodeKey, false); }
|
||||
set { SessionState.SetBool(RecompiledUnsupportedChangesInPlaymodeKey, value); }
|
||||
}
|
||||
|
||||
public static bool EditorCodePatcherInit {
|
||||
get { return SessionState.GetBool(EditorCodePatcherInitKey, false); }
|
||||
set { SessionState.SetBool(EditorCodePatcherInitKey, value); }
|
||||
}
|
||||
|
||||
public static bool ShowedDebuggerCompatibility {
|
||||
get { return SessionState.GetBool(ShowedDebuggerCompatibilityKey, false); }
|
||||
set { SessionState.SetBool(ShowedDebuggerCompatibilityKey, value); }
|
||||
}
|
||||
|
||||
public static bool DisallowedAutoRefresh {
|
||||
get { return SessionState.GetBool(DisallowedAutoRefreshKey, false); }
|
||||
set { SessionState.SetBool(DisallowedAutoRefreshKey, value); }
|
||||
}
|
||||
|
||||
public static bool WarnedDebuggerAttached {
|
||||
get { return SessionState.GetBool(WarnedDebuggerAttachedKey, false); }
|
||||
set { SessionState.SetBool(WarnedDebuggerAttachedKey, value); }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 803347281bcf46b6b37d48231b8882be
|
||||
timeCreated: 1694458889
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs
|
||||
uploadId: 870414
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,154 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90cf8e542151548c6aa3cba26467e144
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 12
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMasterTextureLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 1
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 3
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Server
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: Android
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 3
|
||||
buildTarget: iPhone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
nameFileIdTable: {}
|
||||
spritePackingTag:
|
||||
pSDRemoveMatte: 0
|
||||
pSDShowRemoveMatteOption: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,117 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 235343744f6348acb629d549ccafff0b
|
||||
timeCreated: 1708187279
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12e88a0f97924d18859867b0cc957d03
|
||||
timeCreated: 1676802469
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
static class DownloadUtility {
|
||||
const string baseUrl = "https://cdn.hotreload.net";
|
||||
|
||||
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 = HttpClientUtils.CreateHttpClient()) {
|
||||
client.Timeout = TimeSpan.FromMinutes(10);
|
||||
return await client.DownloadAsync(url, targetFilePath, progress, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPackagePrefix(string version, string locale) {
|
||||
if (PackageConst.IsAssetStoreBuild) {
|
||||
return $"releases/asset-store/{(locale == Locale.SimplifiedChinese ? "zh/" : "")}{version.Replace('.', '-')}";
|
||||
}
|
||||
return $"releases/{(locale == Locale.SimplifiedChinese ? "zh/" : "")}{version.Replace('.', '-')}";
|
||||
}
|
||||
|
||||
public static string GetDownloadUrl(string key) {
|
||||
return $"{baseUrl}/{key}";
|
||||
}
|
||||
|
||||
public static async Task<DownloadResult> DownloadAsync(this HttpClient client, string requestUri, string destinationFilePath, IProgress<float> progress, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||
// Get the http headers first to examine the content length
|
||||
using (var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false)) {
|
||||
if (response.StatusCode != HttpStatusCode.OK) {
|
||||
throw new DownloadException(string.Format(Translations.Errors.ExceptionDownloadFailed, response.StatusCode, response.ReasonPhrase));
|
||||
}
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
if (!contentLength.HasValue) {
|
||||
throw new DownloadException(Translations.Errors.ExceptionDownloadContentLengthUnknown);
|
||||
}
|
||||
|
||||
using (var fs = new FileStream(destinationFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
|
||||
using (var download = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) {
|
||||
|
||||
// Ignore progress reporting when no progress reporter was
|
||||
if (progress == null) {
|
||||
await download.CopyToAsync(fs).ConfigureAwait(false);
|
||||
} else {
|
||||
// Convert absolute progress (bytes downloaded) into relative progress (0% - 99.9%)
|
||||
var relativeProgress = new Progress<long>(totalBytes => progress.Report(Math.Min(99.9f, (float)totalBytes / contentLength.Value)));
|
||||
// Use extension method to report progress while downloading
|
||||
await download.CopyToAsync(fs, 81920, relativeProgress, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
await fs.FlushAsync().ConfigureAwait(false);
|
||||
if (fs.Length != contentLength.Value) {
|
||||
throw new DownloadException(Translations.Errors.ExceptionDownloadFileCorrupted);
|
||||
}
|
||||
return new DownloadResult(HttpStatusCode.OK, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long> progress, CancellationToken cancellationToken) {
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
if (!source.CanRead)
|
||||
throw new ArgumentException(Translations.Utility.StreamHasToBeReadable, nameof(source));
|
||||
if (destination == null)
|
||||
throw new ArgumentNullException(nameof(destination));
|
||||
if (!destination.CanWrite)
|
||||
throw new ArgumentException(Translations.Utility.StreamHasToBeWritable, nameof(destination));
|
||||
if (bufferSize < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||||
|
||||
var buffer = new byte[bufferSize];
|
||||
long totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) {
|
||||
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
|
||||
totalBytesRead += bytesRead;
|
||||
progress?.Report(totalBytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DownloadException : ApplicationException {
|
||||
public DownloadException(string message)
|
||||
: base(message) {
|
||||
}
|
||||
|
||||
public DownloadException(string message, Exception innerException)
|
||||
: base(message, innerException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a7a39befa1f455cb21fcad46513b6e5
|
||||
timeCreated: 1676973096
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
static class ExponentialBackoff {
|
||||
|
||||
public static TimeSpan GetTimeout(int attempt, int minBackoff = 250, int maxBackoff = 60000, int deltaBackoff = 400) {
|
||||
attempt = Math.Min(25, attempt); // safety to avoid overflow below
|
||||
|
||||
var delta = (uint)(
|
||||
(Math.Pow(2.0, attempt) - 1.0)
|
||||
* deltaBackoff
|
||||
);
|
||||
|
||||
var interval = Math.Min(checked(minBackoff + delta), maxBackoff);
|
||||
return TimeSpan.FromMilliseconds(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5329de48151140eb871721ae80f925cd
|
||||
timeCreated: 1676908147
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using SingularityGroup.HotReload.EditorDependencies;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
#if UNITY_2019_4_OR_NEWER
|
||||
using System.Reflection;
|
||||
using Unity.CodeEditor;
|
||||
#endif
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
static class InstallUtility {
|
||||
static string installFlagPath = PackageConst.LibraryCachePath + "/installFlag.txt";
|
||||
|
||||
public static void DebugClearInstallState() {
|
||||
File.Delete(installFlagPath);
|
||||
}
|
||||
|
||||
// HandleEditorStart is only called on editor start, not on domain reload
|
||||
public static void HandleEditorStart(string updatedFromVersion) {
|
||||
var showOnStartup = HotReloadPrefs.ShowOnStartup;
|
||||
if (showOnStartup == ShowOnStartupEnum.Always || (showOnStartup == ShowOnStartupEnum.OnNewVersion && !String.IsNullOrEmpty(updatedFromVersion))) {
|
||||
if (!HotReloadPrefs.DeactivateHotReload) {
|
||||
HotReloadWindow.Open();
|
||||
}
|
||||
}
|
||||
if (HotReloadPrefs.LaunchOnEditorStart && !HotReloadPrefs.DeactivateHotReload) {
|
||||
EditorCodePatcher.DownloadAndRun().Forget();
|
||||
}
|
||||
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Editor, StatEventType.Start)).Forget();
|
||||
}
|
||||
|
||||
public static void CheckForNewInstall() {
|
||||
if(File.Exists(installFlagPath) || MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(installFlagPath));
|
||||
using(File.Create(installFlagPath)) { }
|
||||
//Avoid opening the window on domain reload
|
||||
EditorApplication.delayCall += HandleNewInstall;
|
||||
}
|
||||
|
||||
static void HandleNewInstall() {
|
||||
if (EditorCodePatcher.licenseType == UnityLicenseType.UnityPro) {
|
||||
RedeemLicenseHelper.I.StartRegistration();
|
||||
}
|
||||
HotReloadPrefs.AllowDisableUnityAutoRefresh = true;
|
||||
HotReloadPrefs.AllAssetChanges = true;
|
||||
HotReloadPrefs.AutoRecompileUnsupportedChanges = true;
|
||||
HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode = true;
|
||||
#if UNITY_EDITOR_WIN
|
||||
HotReloadPrefs.UseWatchman = false;
|
||||
#endif
|
||||
if (HotReloadCli.CanOpenInBackground) {
|
||||
HotReloadPrefs.DisableConsoleWindow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee93b2c98bc7d8f4bb38bbbd5961d354
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal class ServerDownloader : IProgress<float> {
|
||||
public float Progress {get; private set;}
|
||||
public bool Started {get; private set;}
|
||||
public int Attempts { get; private set; }
|
||||
|
||||
class Config {
|
||||
public Dictionary<string, string> customServerExecutables;
|
||||
}
|
||||
|
||||
public string GetExecutablePath(ICliController cliController) {
|
||||
var targetDir = CliUtils.GetExecutableTargetDir();
|
||||
var targetPath = Path.Combine(targetDir, cliController.BinaryFileName);
|
||||
return targetPath;
|
||||
}
|
||||
|
||||
public bool IsDownloaded(ICliController cliController) {
|
||||
return File.Exists(GetExecutablePath(cliController));
|
||||
}
|
||||
|
||||
public string GetBinaryPath(ICliController cliController) {
|
||||
var defaultExecutablePath = CliUtils.GetExecutableTargetDir();
|
||||
var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFilePath));
|
||||
var customExecutables = config?.customServerExecutables;
|
||||
if (customExecutables == null) {
|
||||
return defaultExecutablePath;
|
||||
}
|
||||
|
||||
string customBinaryPath;
|
||||
if (!customExecutables.TryGetValue(cliController.PlatformName, out customBinaryPath)) {
|
||||
return defaultExecutablePath;
|
||||
}
|
||||
return Path.GetDirectoryName(customBinaryPath);
|
||||
}
|
||||
|
||||
public bool CheckIfDownloaded(ICliController cliController) {
|
||||
if(TryUseUserDefinedBinaryPath(cliController, GetExecutablePath(cliController))) {
|
||||
Started = true;
|
||||
Progress = 1f;
|
||||
return true;
|
||||
} else if(IsDownloaded(cliController)) {
|
||||
Started = true;
|
||||
Progress = 1f;
|
||||
return true;
|
||||
} else {
|
||||
Started = false;
|
||||
Progress = 0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> EnsureDownloaded(ICliController cliController, CancellationToken cancellationToken, int maxAttempts = -1) {
|
||||
var targetDir = CliUtils.GetExecutableTargetDir();
|
||||
var targetPath = Path.Combine(targetDir, cliController.BinaryFileName);
|
||||
Started = true;
|
||||
Progress = 0f;
|
||||
Attempts = 0;
|
||||
if(File.Exists(targetPath)) {
|
||||
Progress = 1f;
|
||||
return true;
|
||||
}
|
||||
await ThreadUtility.SwitchToThreadPool(cancellationToken);
|
||||
|
||||
Directory.CreateDirectory(targetDir);
|
||||
if(TryUseUserDefinedBinaryPath(cliController, targetPath, true)) {
|
||||
Progress = 1f;
|
||||
return true;
|
||||
}
|
||||
|
||||
var tmpPath = CliUtils.GetTempDownloadFilePath("Server.tmp");
|
||||
bool sucess = false;
|
||||
HashSet<string> errors = null;
|
||||
while(!sucess) {
|
||||
try {
|
||||
if (File.Exists(targetPath)) {
|
||||
Progress = 1f;
|
||||
Attempts = 0;
|
||||
return true;
|
||||
}
|
||||
// Note: we are writing to temp file so if downloaded file is corrupted it will not cause issues until it's copied to target location
|
||||
var result = await DownloadUtility.DownloadFile(GetDownloadUrl(cliController), tmpPath, this, cancellationToken).ConfigureAwait(false);
|
||||
sucess = result.statusCode == HttpStatusCode.OK;
|
||||
} catch (OperationCanceledException) {
|
||||
Progress = 0;
|
||||
Started = false;
|
||||
Attempts = 0;
|
||||
throw;
|
||||
} catch (Exception e) {
|
||||
var error = $"{e.GetType().Name}: {e.Message}";
|
||||
errors = (errors ?? new HashSet<string>());
|
||||
if (errors.Add(error)) {
|
||||
Log.Warning(Translations.Errors.ErrorDownloadFailed, error);
|
||||
}
|
||||
}
|
||||
if (!sucess) {
|
||||
if (maxAttempts > 0 && Attempts + 1 >= maxAttempts) {
|
||||
Progress = 0;
|
||||
Attempts = 0;
|
||||
Started = false;
|
||||
Log.Warning(Translations.Errors.ErrorDownloadFailedMaxAttempts);
|
||||
return false;
|
||||
}
|
||||
await Task.Delay(ExponentialBackoff.GetTimeout(Attempts++), cancellationToken).ConfigureAwait(false);
|
||||
Progress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (errors?.Count > 0) {
|
||||
var data = new EditorExtraData {
|
||||
{ StatKey.Errors, new List<string>(errors) },
|
||||
};
|
||||
// sending telemetry requires server to be running so we only attempt after server is downloaded
|
||||
RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Editor, StatEventType.Download), data).Forget();
|
||||
Log.Info(Translations.Errors.ErrorDownloadSucceeded);
|
||||
}
|
||||
|
||||
const int ERROR_ALREADY_EXISTS = 0xB7;
|
||||
try {
|
||||
File.Move(tmpPath, targetPath);
|
||||
} catch(IOException ex) when((ex.HResult & 0x0000FFFF) == ERROR_ALREADY_EXISTS) {
|
||||
//another downloader came first
|
||||
try {
|
||||
File.Delete(tmpPath);
|
||||
} catch {
|
||||
//ignored
|
||||
}
|
||||
}
|
||||
Progress = 1f;
|
||||
Attempts = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryUseUserDefinedBinaryPath(ICliController cliController, string targetPath, bool logNotFoundWarning = false) {
|
||||
if (!File.Exists(PackageConst.ConfigFilePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFilePath));
|
||||
var customExecutables = config?.customServerExecutables;
|
||||
if (customExecutables == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string customBinaryPath;
|
||||
if(!customExecutables.TryGetValue(cliController.PlatformName, out customBinaryPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!File.Exists(customBinaryPath)) {
|
||||
if (logNotFoundWarning) {
|
||||
Log.Warning(Translations.Errors.ErrorServerBinaryNotFound, cliController.PlatformName, customBinaryPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var targetFile = new FileInfo(targetPath);
|
||||
bool copy = true;
|
||||
if (targetFile.Exists) {
|
||||
copy = File.GetLastWriteTimeUtc(customBinaryPath) > targetFile.LastWriteTimeUtc;
|
||||
}
|
||||
if (copy) {
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
|
||||
File.Copy(customBinaryPath, targetPath, true);
|
||||
}
|
||||
return true;
|
||||
} catch(IOException ex) {
|
||||
Log.Warning(Translations.Errors.ErrorCopyingServerBinary, customBinaryPath, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDownloadUrl(ICliController cliController) {
|
||||
const string version = PackageConst.ServerVersion;
|
||||
// NOTE: server is not translated at the moment so we always use english
|
||||
var key = $"{DownloadUtility.GetPackagePrefix(version, Locale.English)}/server/{cliController.PlatformName}/{cliController.BinaryFileName}";
|
||||
return DownloadUtility.GetDownloadUrl(key);
|
||||
}
|
||||
|
||||
void IProgress<float>.Report(float value) {
|
||||
Progress = value;
|
||||
}
|
||||
|
||||
public async Task<bool> PromptForDownload(CancellationToken manualDownloadCancelationToken) {
|
||||
if (EditorUtility.DisplayDialog(
|
||||
title: Translations.Dialogs.DialogTitleInstallComponents,
|
||||
message: Translations.Dialogs.DialogMessageInstallComponents,
|
||||
ok: Translations.Dialogs.DialogButtonInstall,
|
||||
cancel: Translations.Dialogs.DialogButtonMoreInfo)
|
||||
) {
|
||||
try {
|
||||
return await EnsureDownloaded(HotReloadCli.controller, manualDownloadCancelationToken);
|
||||
} catch (Exception ex) {
|
||||
if (ex is OperationCanceledException || manualDownloadCancelationToken.IsCancellationRequested) {
|
||||
// binary was downloaded manually. Retry once to detect the binary.
|
||||
return await EnsureDownloaded(HotReloadCli.controller, CancellationToken.None, 1);
|
||||
}
|
||||
Log.Exception(ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadResult {
|
||||
public readonly HttpStatusCode statusCode;
|
||||
public readonly string error;
|
||||
public DownloadResult(HttpStatusCode statusCode, string error) {
|
||||
this.statusCode = statusCode;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f076514e142a4915ab2676a9ca6d884a
|
||||
timeCreated: 1676802482
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using SingularityGroup.HotReload.RuntimeDependencies;
|
||||
using UnityEditor;
|
||||
#if UNITY_EDITOR_WIN
|
||||
using System.Diagnostics;
|
||||
using Debug = UnityEngine.Debug;
|
||||
#endif
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
static class UpdateUtility {
|
||||
public static async Task<string> Update(string version, IProgress<float> progress, CancellationToken cancellationToken) {
|
||||
await ThreadUtility.SwitchToThreadPool();
|
||||
|
||||
string serverDir;
|
||||
if(!CliUtils.TryFindServerDir(out serverDir)) {
|
||||
progress?.Report(1);
|
||||
return Translations.Utility.UnableToLocateHotReloadPackage;
|
||||
}
|
||||
var packageDir = Path.GetDirectoryName(Path.GetFullPath(serverDir));
|
||||
var cacheDir = Path.GetFullPath(PackageConst.LibraryCachePath);
|
||||
if(Path.GetPathRoot(packageDir) != Path.GetPathRoot(cacheDir)) {
|
||||
progress?.Report(1);
|
||||
return Translations.Utility.UnableToUpdatePackageDifferentDrive;
|
||||
}
|
||||
var updatedPackageCopy = BackupPackage(packageDir, version);
|
||||
|
||||
var key = $"{DownloadUtility.GetPackagePrefix(version, PackageConst.DefaultLocale)}/HotReload.zip";
|
||||
var url = DownloadUtility.GetDownloadUrl(key);
|
||||
var targetFileName = $"HotReload{version.Replace('.', '-')}.zip";
|
||||
var targetFilePath = CliUtils.GetTempDownloadFilePath(targetFileName);
|
||||
var proxy = new Progress<float>(f => progress?.Report(f * 0.7f));
|
||||
var result = await DownloadUtility.DownloadFile(url, targetFilePath, proxy, cancellationToken).ConfigureAwait(false);
|
||||
if(result.error != null) {
|
||||
progress?.Report(1);
|
||||
return result.error;
|
||||
}
|
||||
|
||||
PackageUpdater.UpdatePackage(targetFilePath, updatedPackageCopy);
|
||||
progress?.Report(0.8f);
|
||||
|
||||
var packageRecycleBinDir = PackageConst.LibraryCachePath + $"/PackageArchived-{version}-{Guid.NewGuid():N}";
|
||||
try {
|
||||
Directory.Move(packageDir, packageRecycleBinDir);
|
||||
Directory.Move(updatedPackageCopy, packageDir);
|
||||
} catch {
|
||||
// fallback to replacing files individually if access to the folder is denied
|
||||
PackageUpdater.UpdatePackage(targetFilePath, packageDir);
|
||||
}
|
||||
try {
|
||||
Directory.Delete(packageRecycleBinDir, true);
|
||||
} catch (IOException) {
|
||||
//ignored
|
||||
}
|
||||
|
||||
progress?.Report(1);
|
||||
return null;
|
||||
}
|
||||
|
||||
static string BackupPackage(string packageDir, string version) {
|
||||
var backupPath = PackageConst.LibraryCachePath + $"/PackageBackup-{version}";
|
||||
if(Directory.Exists(backupPath)) {
|
||||
Directory.Delete(backupPath, true);
|
||||
}
|
||||
DirectoryCopy(packageDir, backupPath);
|
||||
return backupPath;
|
||||
}
|
||||
|
||||
static void DirectoryCopy(string sourceDirPath, string destDirPath) {
|
||||
var rootSource = new DirectoryInfo(sourceDirPath);
|
||||
|
||||
var sourceDirs = rootSource.GetDirectories();
|
||||
// ensure destination directory exists
|
||||
Directory.CreateDirectory(destDirPath);
|
||||
|
||||
// Get the files in the directory and copy them to the new destination
|
||||
var files = rootSource.GetFiles();
|
||||
foreach (var file in files) {
|
||||
string temppath = Path.Combine(destDirPath, file.Name);
|
||||
var copy = file.CopyTo(temppath);
|
||||
copy.LastWriteTimeUtc = file.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
// copying subdirectories, and their contents to destination
|
||||
foreach (var subdir in sourceDirs) {
|
||||
string subDirDestPath = Path.Combine(destDirPath, subdir.Name);
|
||||
DirectoryCopy(subdir.FullName, subDirDestPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8485ce38122465e9e70d5992d9ae7ed
|
||||
timeCreated: 1676966641
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c856b3b3ce5f41c7aaebf7b543be697a
|
||||
timeCreated: 1759652305
|
||||
@@ -0,0 +1,103 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class About {
|
||||
// Log Messages
|
||||
public static string LogNoLogsFound;
|
||||
public static string LogFailedOpeningLogFile;
|
||||
public static string LogBuildTargetSwitching;
|
||||
|
||||
// About/Help Tab
|
||||
public static string AboutTitle;
|
||||
public static string AboutDescription;
|
||||
public static string AboutVersionInfo;
|
||||
public static string AboutChangelog;
|
||||
public static string AboutFeatures;
|
||||
public static string AboutImprovements;
|
||||
public static string AboutFixes;
|
||||
public static string AboutToday;
|
||||
public static string AboutYesterday;
|
||||
public static string AboutDaysAgo;
|
||||
public static string AboutOneMonthAgo;
|
||||
public static string AboutMonthsAgo;
|
||||
public static string AboutOneYearAgo;
|
||||
public static string AboutYearsAgo;
|
||||
|
||||
// About Tab Buttons
|
||||
public static string ButtonDocumentation;
|
||||
public static string ButtonContact;
|
||||
public static string ButtonUnityForum;
|
||||
public static string ButtonReportIssue;
|
||||
public static string ButtonJoinDiscord;
|
||||
public static string ButtonSeeMore;
|
||||
public static string ButtonManageLicense;
|
||||
public static string ButtonManageAccount;
|
||||
|
||||
public static void LoadEnglish() {
|
||||
// Log Messages
|
||||
LogNoLogsFound = "No logs found";
|
||||
LogFailedOpeningLogFile = "Failed opening log file.";
|
||||
LogBuildTargetSwitching = "Build target is switching to {0}.";
|
||||
|
||||
// About/Help Tab
|
||||
AboutTitle = "Help";
|
||||
AboutDescription = "Info and support for Hot Reload for Unity.";
|
||||
AboutVersionInfo = " Hot Reload version {0}. ";
|
||||
AboutChangelog = "Changelog";
|
||||
AboutFeatures = "Features:";
|
||||
AboutImprovements = "Improvements:";
|
||||
AboutFixes = "Fixes:";
|
||||
AboutToday = "Today";
|
||||
AboutYesterday = "Yesterday";
|
||||
AboutDaysAgo = "{0} days ago";
|
||||
AboutOneMonthAgo = "one month ago";
|
||||
AboutMonthsAgo = "{0} months ago";
|
||||
AboutOneYearAgo = "one year ago";
|
||||
AboutYearsAgo = "{0} years ago";
|
||||
|
||||
// About Tab Buttons
|
||||
ButtonDocumentation = "Documentation";
|
||||
ButtonContact = "Contact";
|
||||
ButtonUnityForum = "Unity Forum";
|
||||
ButtonReportIssue = "Report issue";
|
||||
ButtonJoinDiscord = "Join Discord";
|
||||
ButtonSeeMore = "See More";
|
||||
ButtonManageLicense = "Manage License";
|
||||
ButtonManageAccount = "Manage Account";
|
||||
}
|
||||
|
||||
public static void LoadSimplifiedChinese() {
|
||||
// Log Messages
|
||||
LogNoLogsFound = "未找到日志";
|
||||
LogFailedOpeningLogFile = "打开日志文件失败。";
|
||||
LogBuildTargetSwitching = "构建目标正在切换到 {0}。";
|
||||
|
||||
// About/Help Tab
|
||||
AboutTitle = "帮助";
|
||||
AboutDescription = "Unity Hot Reload 的信息和支持。";
|
||||
AboutVersionInfo = " Hot Reload 版本 {0}。";
|
||||
AboutChangelog = "更新日志";
|
||||
AboutFeatures = "新功能:";
|
||||
AboutImprovements = "改进:";
|
||||
AboutFixes = "修复:";
|
||||
AboutToday = "今天";
|
||||
AboutYesterday = "昨天";
|
||||
AboutDaysAgo = "{0} 天前";
|
||||
AboutOneMonthAgo = "一个月前";
|
||||
AboutMonthsAgo = "{0} 个月前";
|
||||
AboutOneYearAgo = "一年前";
|
||||
AboutYearsAgo = "{0} 年前";
|
||||
|
||||
// About Tab Buttons
|
||||
ButtonDocumentation = "文档";
|
||||
ButtonContact = "联系我们";
|
||||
ButtonUnityForum = "Unity 论坛";
|
||||
ButtonReportIssue = "报告问题";
|
||||
ButtonJoinDiscord = "加入 Discord";
|
||||
ButtonSeeMore = "查看更多";
|
||||
ButtonManageLicense = "管理许可证";
|
||||
ButtonManageAccount = "管理帐户";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 155269da640bb89428bf5b3d415878c9
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,118 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class Common {
|
||||
// Common Buttons
|
||||
public static string ButtonSubmit;
|
||||
public static string ButtonHide;
|
||||
public static string ButtonClear;
|
||||
public static string ButtonStart;
|
||||
public static string ButtonStop;
|
||||
public static string ButtonRecompile;
|
||||
public static string ButtonProceed;
|
||||
public static string ButtonYes;
|
||||
public static string ButtonNo;
|
||||
public static string ButtonCancel;
|
||||
public static string ButtonOpenInBrowser;
|
||||
public static string ButtonLogin;
|
||||
public static string ButtonLogout;
|
||||
public static string ButtonRedeem;
|
||||
public static string ButtonSkip;
|
||||
public static string ButtonUpgrade;
|
||||
public static string ButtonDocs;
|
||||
public static string ButtonFixAll;
|
||||
public static string ButtonGoBack;
|
||||
public static string ButtonActivatePromoCode;
|
||||
public static string ButtonActivateLicense;
|
||||
public static string ButtonOpenLogFile;
|
||||
public static string ButtonBrowseAllLogs;
|
||||
public static string ButtonInfo;
|
||||
public static string ButtonNotNow;
|
||||
public static string ButtonStopAndRecompile;
|
||||
|
||||
// Common Labels
|
||||
public static string LabelEmail;
|
||||
public static string LabelPassword;
|
||||
public static string LabelPromoCode;
|
||||
public static string LabelCompanySize;
|
||||
public static string LabelInvoiceNumber;
|
||||
public static string LabelShowOnStartup;
|
||||
|
||||
public static void LoadEnglish() {
|
||||
// Common Buttons
|
||||
ButtonSubmit = "Submit";
|
||||
ButtonHide = "Hide";
|
||||
ButtonClear = "Clear";
|
||||
ButtonStart = " Start";
|
||||
ButtonStop = " Stop";
|
||||
ButtonRecompile = " Recompile";
|
||||
ButtonProceed = "Proceed";
|
||||
ButtonYes = "Yes";
|
||||
ButtonNo = "No";
|
||||
ButtonCancel = "Cancel";
|
||||
ButtonOpenInBrowser = "Open in browser";
|
||||
ButtonLogin = "Login";
|
||||
ButtonLogout = "Logout";
|
||||
ButtonRedeem = "Redeem";
|
||||
ButtonSkip = "Skip";
|
||||
ButtonUpgrade = "Upgrade";
|
||||
ButtonFixAll = "Fix All";
|
||||
ButtonGoBack = "Go Back";
|
||||
ButtonActivatePromoCode = "Activate promo code";
|
||||
ButtonActivateLicense = "Activate License";
|
||||
ButtonOpenLogFile = "Open Log File";
|
||||
ButtonBrowseAllLogs = "Browse all logs";
|
||||
ButtonInfo = " Info";
|
||||
ButtonNotNow = "Not now";
|
||||
ButtonStopAndRecompile = "Stop and Recompile";
|
||||
|
||||
// Common Labels
|
||||
LabelEmail = "Email";
|
||||
LabelPassword = "Password";
|
||||
LabelPromoCode = "Promo code";
|
||||
LabelCompanySize = "Company size (number of employees)";
|
||||
LabelInvoiceNumber = "Invoice number/Order ID";
|
||||
LabelShowOnStartup = "Show On Startup";
|
||||
ButtonDocs = "Docs";
|
||||
}
|
||||
|
||||
public static void LoadSimplifiedChinese() {
|
||||
// Common Buttons
|
||||
ButtonSubmit = "提交";
|
||||
ButtonHide = "隐藏";
|
||||
ButtonClear = "清除";
|
||||
ButtonStart = " 开始";
|
||||
ButtonStop = " 停止";
|
||||
ButtonRecompile = " 重新编译";
|
||||
ButtonProceed = "继续";
|
||||
ButtonYes = "是";
|
||||
ButtonNo = "否";
|
||||
ButtonCancel = "取消";
|
||||
ButtonOpenInBrowser = "在浏览器中打开";
|
||||
ButtonLogin = "登录";
|
||||
ButtonLogout = "登出";
|
||||
ButtonRedeem = "兑换";
|
||||
ButtonSkip = "跳过";
|
||||
ButtonDocs = "文档";
|
||||
ButtonUpgrade = "升级";
|
||||
ButtonFixAll = "全部修复";
|
||||
ButtonGoBack = "返回";
|
||||
ButtonActivatePromoCode = "激活促销代码";
|
||||
ButtonActivateLicense = "激活许可证";
|
||||
ButtonOpenLogFile = "打开日志文件";
|
||||
ButtonBrowseAllLogs = "浏览所有日志";
|
||||
ButtonInfo = " 信息";
|
||||
ButtonNotNow = "以后再说";
|
||||
ButtonStopAndRecompile = "停止并重新编译";
|
||||
|
||||
// Common Labels
|
||||
LabelEmail = "电子邮件";
|
||||
LabelPassword = "密码";
|
||||
LabelPromoCode = "促销代码";
|
||||
LabelCompanySize = "公司规模(员工人数)";
|
||||
LabelInvoiceNumber = "发票号码/订单 ID";
|
||||
LabelShowOnStartup = "启动时显示";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fea73e8304102e4a805c3d00653b84b
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,181 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class Dialogs {
|
||||
// Dialogs
|
||||
public static string DialogTitleRecompile;
|
||||
public static string DialogMessageRecompile;
|
||||
public static string DialogTitleStopPlayMode;
|
||||
public static string DialogMessageStopPlayMode;
|
||||
public static string DialogTitleRecoverPassword;
|
||||
public static string DialogMessageRecoverPassword;
|
||||
public static string DialogTitleRateApp;
|
||||
public static string DialogMessageRateApp;
|
||||
public static string DialogTitleDeactivate;
|
||||
public static string DialogMessageDeactivate;
|
||||
public static string DialogButtonDeactivate;
|
||||
public static string DialogTitleRestartServer;
|
||||
public static string DialogMessageRestartAssetRefresh;
|
||||
public static string DialogMessageRestartConsoleWindow;
|
||||
public static string DialogMessageRestartErrorReporting;
|
||||
public static string DialogButtonRestartHotReload;
|
||||
public static string DialogButtonRestartServer;
|
||||
public static string DialogButtonDontRestart;
|
||||
public static string DialogTitleInstallComponents;
|
||||
public static string DialogMessageInstallComponents;
|
||||
public static string DialogButtonInstall;
|
||||
public static string DialogButtonMoreInfo;
|
||||
public static string DialogTitleSwitchBuildTarget;
|
||||
public static string DialogMessageSwitchBuildTarget;
|
||||
public static string DialogButtonSwitchToStandalone;
|
||||
|
||||
// About Tab Dialog Messages
|
||||
public static string DialogManageLicenseMessage;
|
||||
public static string DialogManageAccountMessage;
|
||||
public static string DialogReportIssueMessage;
|
||||
|
||||
// Update Dialog
|
||||
public static string DialogTitleUpdateFormat;
|
||||
public static string DialogMessageUpdateFormat;
|
||||
public static string DialogButtonUpdate;
|
||||
|
||||
// Update Server Dialog
|
||||
public static string DialogMessageRestartUpdate;
|
||||
public static string DialogMessageRestartFieldInitializer;
|
||||
public static string DialogMessageRestartExposeServer;
|
||||
public static string DialogTitleHotReload;
|
||||
|
||||
// Debugger Detected Dialogs
|
||||
public static string DialogTitleHotReloadDebuggerDetected;
|
||||
public static string DialogMessageHotReloadDebuggerDetectedPause;
|
||||
public static string DialogMessageHotReloadDebuggerDetectedRecompile;
|
||||
public static string DialogButtonHotReloadDebuggerDetectedPause;
|
||||
public static string DialogButtonHotReloadDebuggerDetectedRecompile;
|
||||
public static string DialogCloseHotReloadDebuggerDetected;
|
||||
public static string DialogButtonHotReloadDebuggerDetectedAdvancedOptions;
|
||||
public static string DialogTitleHotReloadDebuggerOptions;
|
||||
public static string DialogMessageHotReloadDebuggerOptions;
|
||||
public static string DialogButtonHotReloadDebuggerOptionsContinue;
|
||||
public static string DialogButtonHotReloadDebuggerOptionsCancelPause;
|
||||
public static string DialogButtonHotReloadDebuggerOptionsCancelRecompile;
|
||||
|
||||
public static void LoadEnglish() {
|
||||
// Dialogs
|
||||
DialogTitleRecompile = "Hot Reload auto-applies changes";
|
||||
DialogMessageRecompile = "Using the Recompile button is only necessary when Hot Reload fails to apply your changes. \n\nDo you wish to proceed?";
|
||||
DialogTitleStopPlayMode = "Stop Play Mode and Recompile?";
|
||||
DialogMessageStopPlayMode = "Using the Recompile button will stop Play Mode.\n\nDo you wish to proceed?";
|
||||
DialogTitleRecoverPassword = "Recover password";
|
||||
DialogMessageRecoverPassword = "Use company code 'naughtycult' and the email you signed up with in order to recover your account.";
|
||||
DialogTitleRateApp = "Rate Hot Reload";
|
||||
DialogMessageRateApp = "Thank you for using Hot Reload!\n\nPlease consider leaving a review on the Asset Store to support us.";
|
||||
DialogTitleDeactivate = "Hot Reload";
|
||||
DialogMessageDeactivate = "Hot Reload will be completely deactivated (unusable) until you activate it again.\n\nDo you want to proceed?";
|
||||
DialogButtonDeactivate = "Deactivate";
|
||||
DialogTitleRestartServer = "Hot Reload";
|
||||
DialogMessageRestartAssetRefresh = "When changing 'Asset refresh', the Hot Reload server must be restarted for this to take effect.\nDo you want to restart it now?";
|
||||
DialogMessageRestartConsoleWindow = "When changing 'Hide console window on start', the Hot Reload server must be restarted for this to take effect.\nDo you want to restart it now?";
|
||||
DialogMessageRestartErrorReporting = "When changing 'Disable Detailed Error Reporting', the Hot Reload server must be restarted for this to take effect.\nDo you want to restart it now?";
|
||||
DialogButtonRestartHotReload = "Restart Hot Reload";
|
||||
DialogButtonRestartServer = "Restart server";
|
||||
DialogButtonDontRestart = "Don't restart";
|
||||
DialogTitleInstallComponents = "Install platform specific components";
|
||||
DialogMessageInstallComponents = "For Hot Reload to work, additional components specific to your operating system have to be installed";
|
||||
DialogButtonInstall = "Install";
|
||||
DialogButtonMoreInfo = "More Info";
|
||||
DialogTitleSwitchBuildTarget = "Switch Build Target";
|
||||
DialogMessageSwitchBuildTarget = "Switching the build target can take a while depending on project size.";
|
||||
DialogButtonSwitchToStandalone = "Switch to Standalone";
|
||||
|
||||
// About Tab Dialog Messages
|
||||
DialogManageLicenseMessage = "Upgrade/downgrade/edit your subscription and edit payment info.";
|
||||
DialogManageAccountMessage = "Login with company code 'naughtycult'. Use the email you signed up with. Your initial password was sent to you by email.";
|
||||
DialogReportIssueMessage = "Report issue in our public issue tracker. Requires gitlab.com account (if you don't have one and are not willing to make it, please contact us by other means such as our website).";
|
||||
|
||||
// Update Dialog
|
||||
DialogTitleUpdateFormat = "Update To v{0}";
|
||||
DialogMessageUpdateFormat = "By pressing 'Update' the Hot Reload package will be updated to v{0}";
|
||||
DialogButtonUpdate = "Update";
|
||||
|
||||
// Update Server Dialog
|
||||
DialogMessageRestartUpdate = "When updating Hot Reload, the server must be restarted for the update to take effect.\nDo you want to restart it now?";
|
||||
DialogMessageRestartFieldInitializer = "When changing 'Apply field initializer edits to existing class instances' setting, the Hot Reload server must restart for it to take effect.\nDo you want to restart it now?";
|
||||
DialogMessageRestartExposeServer = "When changing '{0}', the Hot Reload server must be restarted for this to take effect.\nDo you want to restart it now?";
|
||||
DialogTitleHotReload = "Hot Reload";
|
||||
|
||||
// Debugger Detected Dialogs
|
||||
DialogTitleHotReloadDebuggerDetected = "Hot Reload - Debugger Detected";
|
||||
DialogMessageHotReloadDebuggerDetectedPause = "Hot Reload can interfere with debugger breakpoints. We recommend pausing Hot Reload while the debugger is attached for the best debugger experience.";
|
||||
DialogMessageHotReloadDebuggerDetectedRecompile = "Hot Reload can interfere with debugger breakpoints. We recommend using auto full-recompile while the debugger is attached for the best debugger experience.";
|
||||
DialogButtonHotReloadDebuggerDetectedPause = "Pause Hot Reload (Recommended)";
|
||||
DialogButtonHotReloadDebuggerDetectedRecompile = "Use Full Recompile (Recommended)";
|
||||
DialogCloseHotReloadDebuggerDetected = "Close";
|
||||
DialogButtonHotReloadDebuggerDetectedAdvancedOptions = "Advanced Options";
|
||||
DialogTitleHotReloadDebuggerOptions = "Advanced: Keep Hot Reload Active";
|
||||
DialogMessageHotReloadDebuggerOptions = "Breakpoints in Visual Studio may not work reliably when Hot Reload remains active during debugging. This is intended for developers who prefer Hot Reload over full breakpoint support. You can change this anytime in Settings.";
|
||||
DialogButtonHotReloadDebuggerOptionsContinue = "Continue With Limited Debugger";
|
||||
DialogButtonHotReloadDebuggerOptionsCancelPause = "Pause Hot Reload";
|
||||
DialogButtonHotReloadDebuggerOptionsCancelRecompile = "Use Full Recompile";
|
||||
}
|
||||
|
||||
public static void LoadSimplifiedChinese() {
|
||||
// Dialogs
|
||||
DialogTitleRecompile = "Hot Reload 自动应用更改";
|
||||
DialogMessageRecompile = "仅当 Hot Reload 未能应用您的更改时,才需要使用“重新编译”按钮。\n\n您希望继续吗?";
|
||||
DialogTitleStopPlayMode = "停止播放模式并重新编译?";
|
||||
DialogMessageStopPlayMode = "使用“重新编译”按钮将停止播放模式。\n\n您希望继续吗?";
|
||||
DialogTitleRecoverPassword = "恢复密码";
|
||||
DialogMessageRecoverPassword = "使用公司代码 'naughtycult' 和您注册时使用的电子邮件来恢复您的帐户。";
|
||||
DialogTitleRateApp = "为 Hot Reload 评分";
|
||||
DialogMessageRateApp = "感谢您使用 Hot Reload!\n\n请考虑在 Asset Store 上留下评论以支持我们。";
|
||||
DialogTitleDeactivate = "Hot Reload";
|
||||
DialogMessageDeactivate = "Hot Reload 将被完全停用(无法使用),直到您再次激活它。\n\n您希望继续吗?";
|
||||
DialogButtonDeactivate = "停用";
|
||||
DialogTitleRestartServer = "Hot Reload";
|
||||
DialogMessageRestartAssetRefresh = "更改“资源刷新”时,必须重新启动 Hot Reload 服务器才能生效。\n您希望现在重新启动吗?";
|
||||
DialogMessageRestartConsoleWindow = "更改“启动时隐藏控制台窗口”时,必须重新启动 Hot Reload 服务器才能生效。\n您希望现在重新启动吗?";
|
||||
DialogMessageRestartErrorReporting = "更改“禁用详细错误报告”时,必须重新启动 Hot Reload 服务器才能生效。\n您希望现在重新启动吗?";
|
||||
DialogButtonRestartHotReload = "重新启动 Hot Reload";
|
||||
DialogButtonRestartServer = "重新启动服务器";
|
||||
DialogButtonDontRestart = "不重新启动";
|
||||
DialogTitleInstallComponents = "安装特定于平台的组件";
|
||||
DialogMessageInstallComponents = "为了让 Hot Reload 工作,必须安装特定于您操作系统的附加组件";
|
||||
DialogButtonInstall = "安装";
|
||||
DialogButtonMoreInfo = "更多信息";
|
||||
DialogTitleSwitchBuildTarget = "切换构建目标";
|
||||
DialogMessageSwitchBuildTarget = "切换构建目标可能需要一段时间,具体取决于项目大小。";
|
||||
DialogButtonSwitchToStandalone = "切换到独立平台";
|
||||
|
||||
// About Tab Dialog Messages
|
||||
DialogManageLicenseMessage = "升级/降级/编辑您的订阅并编辑付款信息。";
|
||||
DialogManageAccountMessage = "使用公司代码 'naughtycult' 登录。使用您注册时使用的电子邮件。您的初始密码已通过电子邮件发送给您。";
|
||||
DialogReportIssueMessage = "在我们的公共问题跟踪器中报告问题。需要 gitlab.com 帐户(如果您没有并且不愿意创建,请通过其他方式与我们联系,例如我们的网站)。";
|
||||
|
||||
// Update Dialog
|
||||
DialogTitleUpdateFormat = "更新到 v{0}";
|
||||
DialogMessageUpdateFormat = "按下“更新”后,Hot Reload 软件包将更新到 v{0}";
|
||||
DialogButtonUpdate = "更新";
|
||||
|
||||
// Update Server Dialog
|
||||
DialogMessageRestartUpdate = "更新 Hot Reload 时,必须重新启动服务器才能使更新生效。\n您希望现在重新启动吗?";
|
||||
DialogMessageRestartFieldInitializer = "更改“将字段初始化程序编辑应用于现有类实例”设置时,必须重新启动 Hot Reload 服务器才能生效。\n您希望现在重新启动吗?";
|
||||
DialogMessageRestartExposeServer = "更改“{0}”时,必须重新启动 Hot Reload 服务器才能生效。\n您希望现在重新启动吗?";
|
||||
DialogTitleHotReload = "Hot Reload";
|
||||
|
||||
// Debugger Detected Dialogs
|
||||
DialogTitleHotReloadDebuggerDetected = "Hot Reload - 检测到调试器";
|
||||
DialogMessageHotReloadDebuggerDetectedPause = "Hot Reload 可能会干扰调试器断点。我们建议在调试器附加时暂停 Hot Reload 以获得最佳调试体验。";
|
||||
DialogMessageHotReloadDebuggerDetectedRecompile = "Hot Reload 可能会干扰调试器断点。我们建议在调试器附加时使用自动完全重新编译以获得最佳调试体验。";
|
||||
DialogButtonHotReloadDebuggerDetectedPause = "暂停 Hot Reload(推荐)";
|
||||
DialogButtonHotReloadDebuggerDetectedRecompile = "使用完全重新编译(推荐)";
|
||||
DialogCloseHotReloadDebuggerDetected = "关闭";
|
||||
DialogButtonHotReloadDebuggerDetectedAdvancedOptions = "高级选项";
|
||||
DialogTitleHotReloadDebuggerOptions = "高级:保持 Hot Reload 激活";
|
||||
DialogMessageHotReloadDebuggerOptions = "当 Hot Reload 在调试期间保持激活状态时,Visual Studio 中的断点可能无法可靠工作。这适用于更偏好 Hot Reload 而非完整断点支持的开发者。您可以随时在设置中更改此选项。";
|
||||
DialogButtonHotReloadDebuggerOptionsContinue = "继续使用受限调试器";
|
||||
DialogButtonHotReloadDebuggerOptionsCancelPause = "暂停 Hot Reload";
|
||||
DialogButtonHotReloadDebuggerOptionsCancelRecompile = "使用完全重新编译";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18bf971e42f1c6b40a2f22f0cc76411f
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,334 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class Errors {
|
||||
// Error Messages
|
||||
public static string ErrorInvalidInput;
|
||||
public static string ErrorNetworkIssue;
|
||||
public static string ErrorDownloadFailed;
|
||||
public static string ErrorDownloadFailedMaxAttempts;
|
||||
public static string ErrorServerBinaryNotFound;
|
||||
public static string ErrorCopyingServerBinary;
|
||||
public static string ErrorDownloadSucceeded;
|
||||
public static string ErrorContactSupport;
|
||||
public static string ErrorEnterNumber;
|
||||
public static string ErrorEnterEmail;
|
||||
public static string ErrorValidEmail;
|
||||
public static string ErrorEnterPassword;
|
||||
public static string ErrorMailExtensions;
|
||||
public static string ErrorEnterInvoiceNumber;
|
||||
public static string ErrorInvalidEmailAddress;
|
||||
public static string ErrorLicenseInvoiceRedeemed;
|
||||
public static string ErrorEmailAlreadyUsed;
|
||||
public static string ErrorInvoiceNotFound;
|
||||
public static string ErrorInvoiceRefunded;
|
||||
public static string ErrorPromoCodeInvalid;
|
||||
public static string ErrorPromoCodeUsed;
|
||||
public static string ErrorPromoCodeExpired;
|
||||
public static string ErrorLicenseExtended;
|
||||
public static string ErrorPromoCodeActivation;
|
||||
public static string ErrorPromoCodeNetwork;
|
||||
|
||||
// Warning Messages
|
||||
public static string WarningUnityJobHotReloaded;
|
||||
public static string WarningBuildSettingsNotSupported;
|
||||
public static string WarningInlinedMethods;
|
||||
public static string WarningMacOSVersionDetectionFailed;
|
||||
public static string WarningUnexpectedSaveProblem;
|
||||
public static string WarningRedeemStatusUnknown;
|
||||
public static string WarningRedeemUnknownError;
|
||||
public static string WarningFailedToRunServerCommand;
|
||||
public static string WarningVersionCheckException;
|
||||
public static string WarningVersionCheckFailed;
|
||||
public static string WarningUpdateIssueFailed;
|
||||
public static string WarningUpdatePackageFailed;
|
||||
public static string WarningUnableToFindPackage;
|
||||
public static string WarningCompileCheckerIssue;
|
||||
public static string WarningFailedToStartServer;
|
||||
public static string WarningNoSlnFileFound;
|
||||
public static string WarningPreparingBuildInfoFailed;
|
||||
public static string WarningInlineMethodChecker;
|
||||
public static string WarningRefreshingAssetFailed;
|
||||
public static string WarningFailedDeterminingRegistration;
|
||||
public static string WarningRedeemingLicenseFailed;
|
||||
public static string WarningInitializingEventEntries;
|
||||
public static string WarningPersistingEventEntries;
|
||||
public static string WarningIndicationTextNotFound;
|
||||
|
||||
// Info Messages
|
||||
public static string InfoDebuggerAttachedFullRecompile;
|
||||
public static string InfoDebuggerAttachedPauseHotReload;
|
||||
public static string InfoInspectorFieldRecompile;
|
||||
public static string InfoDefaultProjectGeneration;
|
||||
public static string ErrorFreeChargesUnavailable;
|
||||
|
||||
// Exception Messages
|
||||
public static string ExceptionExpectedZipFile;
|
||||
public static string ExceptionZipFileNotFound;
|
||||
public static string ExceptionUnzipFailed;
|
||||
public static string ExceptionDownloadFailed;
|
||||
public static string ExceptionUnableToFindManifest;
|
||||
public static string ErrorRedeemRequestFailed;
|
||||
public static string ErrorFailedDeserializingRedeem;
|
||||
public static string ErrorRedeemingWebException;
|
||||
public static string ExceptionDownloadContentLengthUnknown;
|
||||
public static string ExceptionDownloadFileCorrupted;
|
||||
public static string ExceptionFailedToFindAppDirectory;
|
||||
public static string ExceptionCouldNotStartCodePatcher;
|
||||
|
||||
// Info/Debug Messages
|
||||
public static string InfoManifestSearch;
|
||||
public static string InfoOmitProjectsForPlayerBuild;
|
||||
public static string InfoSeparator;
|
||||
public static string InfoOmittedEditorProject;
|
||||
public static string InfoFoundProjectNamed;
|
||||
|
||||
// Project Generation Warnings
|
||||
public static string WarningPostProcessorException;
|
||||
public static string WarningPostProcessorFailedProject;
|
||||
public static string WarningPostProcessorFailedSolution;
|
||||
public static string WarningPostProcessorNoDefaultConstructor;
|
||||
public static string WarningPostProcessorConstructorException;
|
||||
public static string WarningPostProcessorUnknownException;
|
||||
|
||||
// Parse Errors
|
||||
public static string ErrorParseError;
|
||||
|
||||
// Android Manifest Comments
|
||||
public static string CommentAndroidCleartextPermit;
|
||||
public static string CommentAndroidCleartextDevelopmentOnly;
|
||||
|
||||
// Debug Messages
|
||||
public static string DebugDetouringMethodFailed;
|
||||
|
||||
// Package Update Errors
|
||||
public static string ErrorRequestFailedStatusCode;
|
||||
public static string ErrorInvalidPackageJson;
|
||||
public static string ErrorInvalidVersionInPackageJson;
|
||||
public static string ErrorUnableToFindManifestJson;
|
||||
public static string ErrorNoDependenciesInManifest;
|
||||
public static string ErrorDependenciesNullInManifest;
|
||||
public static string ErrorNoDependenciesSpecified;
|
||||
|
||||
public static void LoadEnglish() {
|
||||
// Error Messages
|
||||
ErrorInvalidInput = "Invalid input";
|
||||
ErrorNetworkIssue = "Something went wrong. Please check your internet connection.";
|
||||
ErrorContactSupport = "Something went wrong. Please contact support if the issue persists.";
|
||||
ErrorDownloadFailed = "Download attempt failed. If the issue persists please reach out to customer support for assistance. Exception: {0}";
|
||||
ErrorDownloadFailedMaxAttempts = "Download attempt failed. If the issue persists please reach out to customer support for assistance.";
|
||||
ErrorServerBinaryNotFound = "unable to find server binary for platform '{0}' at '{1}'. Will proceed with downloading the binary (default behavior)";
|
||||
ErrorCopyingServerBinary = "encountered exception when copying server binary in the specified custom executable path '{0}':\n{1}";
|
||||
ErrorDownloadSucceeded = "Download succeeded!";
|
||||
ErrorEnterNumber = "Please enter a number.";
|
||||
ErrorEnterEmail = "Please enter your email address.";
|
||||
ErrorValidEmail = "Please enter a valid email address.";
|
||||
ErrorEnterPassword = "Please enter your password.";
|
||||
ErrorMailExtensions = "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.";
|
||||
ErrorEnterInvoiceNumber = "Please enter invoice number / order ID.";
|
||||
ErrorInvalidEmailAddress = "Please enter a valid email address.";
|
||||
ErrorLicenseInvoiceRedeemed = "The invoice number/order ID you're trying to use has already been applied to redeem a license. Please enter a different invoice number/order ID. If you have already redeemed a license for another email, you may proceed to the next step.";
|
||||
ErrorEmailAlreadyUsed = "The provided email has already been used to redeem a license. If you have previously redeemed a license, you can proceed to the next step and use your existing credentials. If not, please input a different email address.";
|
||||
ErrorInvoiceNotFound = "The invoice was not found. Please ensure that you've entered the correct invoice number/order ID.";
|
||||
ErrorInvoiceRefunded = "The purchase has been refunded. Please enter a different invoice number/order ID.";
|
||||
ErrorPromoCodeInvalid = "Your promo code is invalid. Please ensure that you have entered the correct promo code.";
|
||||
ErrorPromoCodeUsed = "Your promo code has already been used.";
|
||||
ErrorPromoCodeExpired = "Your promo code has expired.";
|
||||
ErrorLicenseExtended = "Your license has already been activated with a promo code. Only one promo code activation per license is allowed.";
|
||||
ErrorPromoCodeActivation = "We encountered an error while activating your promo code. Please try again. If the issue persists, please contact our customer support team for assistance.";
|
||||
ErrorPromoCodeNetwork = "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists.";
|
||||
|
||||
// Warning Messages
|
||||
WarningUnityJobHotReloaded = "A unity job was hot reloaded. This will cause a harmless warning that can be ignored. More info about this can be found here: {0}";
|
||||
WarningBuildSettingsNotSupported = "Hot Reload was not included in the build because one or more build settings were not supported.";
|
||||
WarningInlinedMethods = "Unity Editor inlines simple methods when it's in \"Release\" mode, which Hot Reload cannot patch.\n\nSwitch to Debug mode to avoid this problem, or let Hot Reload fully recompile Unity when this issue occurs.";
|
||||
WarningMacOSVersionDetectionFailed = "Failed to detect MacOS version, if Hot Reload fails to start, please contact support.";
|
||||
WarningUnexpectedSaveProblem = "Unexpected problem unable to save HotReloadSettingsObject";
|
||||
WarningRedeemStatusUnknown = "Redeeming license failed: unknown status received";
|
||||
WarningRedeemUnknownError = "Redeeming a license failed: uknown error encountered";
|
||||
WarningFailedToRunServerCommand = "Failed to the run the start server command. ExitCode={0}\nFilepath: {1}";
|
||||
WarningVersionCheckException = "encountered exception when checking for new Hot Reload package version:\n{0}";
|
||||
WarningVersionCheckFailed = "version check failed: {0}";
|
||||
WarningUpdateIssueFailed = "Encountered issue when updating Hot Reload: {0}";
|
||||
WarningUpdatePackageFailed = "Failed to update package: {0}";
|
||||
WarningUnableToFindPackage = "Unable to find package. message: {0}";
|
||||
WarningCompileCheckerIssue = "compile checker encountered issue: {0} {1}";
|
||||
WarningFailedToStartServer = "Failed to start the Hot Reload Server. {0}";
|
||||
WarningNoSlnFileFound = "No .sln file found. Open any c# file to generate it so Hot Reload can work properly";
|
||||
WarningPreparingBuildInfoFailed = "Preparing build info failed! On-device functionality might not work. Exception: {0}";
|
||||
WarningInlineMethodChecker = "Inline method checker ran into an exception. Please contact support with the exception message to investigate the problem. Exception: {0}";
|
||||
WarningRefreshingAssetFailed = "Refreshing asset at path: {0} failed due to exception: {1}";
|
||||
WarningFailedDeterminingRegistration = "Failed determining registration outcome with {0}: {1}";
|
||||
WarningRedeemingLicenseFailed = "Redeeming a license failed with error: {0}";
|
||||
WarningInitializingEventEntries = "Failed initializing Hot Reload event entries on start: {0}";
|
||||
WarningPersistingEventEntries = "Failed persisting Hot Reload event entries: {0}";
|
||||
WarningIndicationTextNotFound = "Indication text not found for status {0}";
|
||||
|
||||
// Info Messages
|
||||
InfoDebuggerAttachedFullRecompile = "A debugger was attached. Pausing Hot Reload and switching to Full Recompile mode to avoid interference with the debugger session. Hot Reload will automatically resume on debugger detach.";
|
||||
InfoDebuggerAttachedPauseHotReload = "A debugger was attached. Pausing Hot Reload to avoid interference with the debugger session. Hot Reload will automatically resume on debugger detach.";
|
||||
InfoInspectorFieldRecompile = "Some inspector field changes require recompilation in Unity. Auto recompiling Unity according to the settings.";
|
||||
InfoDefaultProjectGeneration = "Using default project generation. If you encounter any problem with Unity's default project generation consider disabling it to use custom project generation.";
|
||||
ErrorFreeChargesUnavailable = "Free charges unavailabe. Please contact support if the issue persists.";
|
||||
|
||||
// Exception Messages
|
||||
ExceptionExpectedZipFile = "Expected to end with .zip, but it was: {0}";
|
||||
ExceptionZipFileNotFound = "zip file not found {0}";
|
||||
ExceptionUnzipFailed = "unzip failed with ExitCode {0}";
|
||||
ExceptionDownloadFailed = "Download failed with status code {0} and reason {1}";
|
||||
ExceptionUnableToFindManifest = "[{0}] Unable to find {1}";
|
||||
ErrorRedeemRequestFailed = "Redeem request failed. Status code: {0}, reason: {1}";
|
||||
ErrorFailedDeserializingRedeem = "Failed deserializing redeem response with exception: {0}: {1}";
|
||||
ErrorRedeemingWebException = "Redeeming license failed: WebException encountered {0}";
|
||||
ExceptionDownloadContentLengthUnknown = "Download failed: Content length unknown";
|
||||
ExceptionDownloadFileCorrupted = "Download failed: download file is corrupted";
|
||||
ExceptionFailedToFindAppDirectory = "Failed to find .app directory and move it to {0}";
|
||||
ExceptionCouldNotStartCodePatcher = "Could not start code patcher process.";
|
||||
|
||||
// Info/Debug Messages
|
||||
InfoManifestSearch = "Did not find {0} at {1}, searching for manifest file inside {2}";
|
||||
InfoOmitProjectsForPlayerBuild = "To compile C# files same as a Player build, we must omit projects which aren't part of the selected Player build.";
|
||||
InfoSeparator = "---------";
|
||||
InfoOmittedEditorProject = "omitted editor/other project named: {0}";
|
||||
InfoFoundProjectNamed = "found project named {0}";
|
||||
|
||||
// Project Generation Warnings
|
||||
WarningPostProcessorException = "Post processor '{0}' threw exception when calling OnGeneratedCSProjectFilesThreaded:\n{1}";
|
||||
WarningPostProcessorFailedProject = "Post processor '{0}' failed when processing project '{1}':\n{2}";
|
||||
WarningPostProcessorFailedSolution = "Post processor '{0}' failed when processing solution '{1}':\n{2}";
|
||||
WarningPostProcessorNoDefaultConstructor = "The type '{0}' was expected to have a public default constructor but it didn't";
|
||||
WarningPostProcessorConstructorException = "Exception occurred when invoking default constructor of '{0}':\n{1}";
|
||||
WarningPostProcessorUnknownException = "Unknown exception encountered when trying to create post processor '{0}':\n{1}";
|
||||
|
||||
// Parse Errors
|
||||
ErrorParseError = "{0} Parse Error : {1}";
|
||||
|
||||
// Android Manifest Comments
|
||||
CommentAndroidCleartextPermit = "[{0}] Added android:usesCleartextTraffic=\"true\" to permit connecting to the Hot Reload http server running on your machine.";
|
||||
CommentAndroidCleartextDevelopmentOnly = "[{0}] This change only happens in Unity development builds. You can disable this in the Hot Reload settings window.";
|
||||
|
||||
// Debug Messages
|
||||
DebugDetouringMethodFailed = "Detouring {0} method failed. {1} {2}";
|
||||
|
||||
// Package Update Errors
|
||||
ErrorRequestFailedStatusCode = "Request failed with statusCode: {0} {1}";
|
||||
ErrorInvalidPackageJson = "Invalid package.json";
|
||||
ErrorInvalidVersionInPackageJson = "Invalid version in package.json: '{0}'";
|
||||
ErrorUnableToFindManifestJson = "Unable to find manifest.json";
|
||||
ErrorNoDependenciesInManifest = "no dependencies object found in manifest.json";
|
||||
ErrorDependenciesNullInManifest = "dependencies object null in manifest.json";
|
||||
ErrorNoDependenciesSpecified = "no dependencies specified in manifest.json";
|
||||
}
|
||||
|
||||
public static void LoadSimplifiedChinese() {
|
||||
// Error Messages
|
||||
ErrorInvalidInput = "无效输入";
|
||||
ErrorNetworkIssue = "出现问题。请检查您的网络连接。";
|
||||
ErrorContactSupport = "出现问题。如果问题仍然存在,请联系支持。";
|
||||
ErrorDownloadFailed = "下载尝试失败。如果问题仍然存在,请联系客户支持寻求帮助。异常:{0}";
|
||||
ErrorDownloadFailedMaxAttempts = "下载尝试失败。如果问题仍然存在,请联系客户支持寻求帮助。";
|
||||
ErrorServerBinaryNotFound = "无法在“{1}”找到平台“{0}”的服务器二进制文件。将继续下载二进制文件(默认行为)";
|
||||
ErrorCopyingServerBinary = "在指定的自定义可执行路径“{0}”中复制服务器二进制文件时遇到异常:\n{1}";
|
||||
ErrorDownloadSucceeded = "下载成功!";
|
||||
ErrorEnterNumber = "请输入一个数字。";
|
||||
ErrorEnterEmail = "请输入您的电子邮件地址。";
|
||||
ErrorValidEmail = "请输入有效的电子邮件地址。";
|
||||
ErrorEnterPassword = "请输入您的密码。";
|
||||
ErrorMailExtensions = "尚不支持邮件扩展(格式为'username+suffix@example.com')。请提供您原始的电子邮件地址(例如'username@example.com',不含'+suffix'部分),我们正在努力解决此问题。";
|
||||
ErrorEnterInvoiceNumber = "请输入发票号码/订单 ID。";
|
||||
ErrorInvalidEmailAddress = "请输入有效的电子邮件地址。";
|
||||
ErrorLicenseInvoiceRedeemed = "您尝试使用的发票号码/订单 ID 已用于兑换许可证。请输入不同的发票号码/订单 ID。如果您已经为另一个电子邮件兑换了许可证,您可以继续下一步。";
|
||||
ErrorEmailAlreadyUsed = "提供的电子邮件已用于兑换许可证。如果您之前已兑换许可证,您可以继续下一步并使用您现有的凭据。如果没有,请输入不同的电子邮件地址。";
|
||||
ErrorInvoiceNotFound = "未找到发票。请确保您输入了正确的发票号码/订单 ID。";
|
||||
ErrorInvoiceRefunded = "购买已退款。请输入不同的发票号码/订单 ID。";
|
||||
ErrorPromoCodeInvalid = "您的促销代码无效。请确保您输入了正确的促销代码。";
|
||||
ErrorPromoCodeUsed = "您的促销代码已被使用。";
|
||||
ErrorPromoCodeExpired = "您的促销代码已过期。";
|
||||
ErrorLicenseExtended = "您的许可证已使用促销代码激活。每个许可证只允许激活一次促销代码。";
|
||||
ErrorPromoCodeActivation = "激活您的促销代码时遇到错误。请重试。如果问题仍然存在,请联系我们的客户支持团队寻求帮助。";
|
||||
ErrorPromoCodeNetwork = "连接到我们的服务器时出现问题。请检查您的网络连接,如果问题仍然存在,请联系客户支持。";
|
||||
|
||||
// Warning Messages
|
||||
WarningUnityJobHotReloaded = "一个 unity 作业被热重载。这将导致一个可以忽略的无害警告。更多信息可以在这里找到:{0}";
|
||||
WarningBuildSettingsNotSupported = "由于一个或多个构建设置不受支持,Hot Reload 未包含在构建中。";
|
||||
WarningInlinedMethods = "Unity 编辑器在“发布”模式下会内联简单方法,Hot Reload 无法修补。\n\n切换到调试模式以避免此问题,或让 Hot Reload 在出现此问题时完全重新编译 Unity。";
|
||||
WarningMacOSVersionDetectionFailed = "检测 MacOS 版本失败,如果 Hot Reload 启动失败,请联系支持。";
|
||||
WarningUnexpectedSaveProblem = "无法保存 HotReloadSettingsObject 的意外问题";
|
||||
WarningRedeemStatusUnknown = "兑换许可证失败:收到未知状态";
|
||||
WarningRedeemUnknownError = "兑换许可证失败:遇到未知错误";
|
||||
WarningFailedToRunServerCommand = "运行启动服务器命令失败。退出代码={0}\n文件路径:{1}";
|
||||
WarningVersionCheckException = "检查新的 Hot Reload 软件包版本时遇到异常:\n{0}";
|
||||
WarningVersionCheckFailed = "版本检查失败:{0}";
|
||||
WarningUpdateIssueFailed = "更新 Hot Reload 时遇到问题:{0}";
|
||||
WarningUpdatePackageFailed = "更新软件包失败:{0}";
|
||||
WarningUnableToFindPackage = "无法找到软件包。消息:{0}";
|
||||
WarningCompileCheckerIssue = "编译检查器遇到问题:{0} {1}";
|
||||
WarningFailedToStartServer = "启动 Hot Reload 服务器失败。{0}";
|
||||
WarningNoSlnFileFound = "未找到 .sln 文件。打开任何 c# 文件以生成它,以便 Hot Reload 正常工作";
|
||||
WarningPreparingBuildInfoFailed = "准备构建信息失败!设备上功能可能无法工作。异常:{0}";
|
||||
WarningInlineMethodChecker = "内联方法检查器遇到异常。请联系支持并提供异常消息以调查问题。异常:{0}";
|
||||
WarningRefreshingAssetFailed = "刷新路径:{0} 的资产失败,原因异常:{1}";
|
||||
WarningFailedDeterminingRegistration = "确定注册结果失败,{0}:{1}";
|
||||
WarningRedeemingLicenseFailed = "兑换许可证失败,错误:{0}";
|
||||
WarningInitializingEventEntries = "启动时初始化 Hot Reload 事件条目失败:{0}";
|
||||
WarningPersistingEventEntries = "持久化 Hot Reload 事件条目失败:{0}";
|
||||
WarningIndicationTextNotFound = "未找到状态 {0} 的指示文本";
|
||||
|
||||
// Info Messages
|
||||
InfoDebuggerAttachedFullRecompile = "已附加调试器。正在暂停热重载并切换到完全重编译模式,以避免干扰调试器会话。热重载将在调试器分离后自动恢复。";
|
||||
InfoDebuggerAttachedPauseHotReload = "检测到调试器已附加。热重载已暂停,以避免对调试会话造成干扰。当调试器断开连接后,热重载将自动恢复。";
|
||||
InfoInspectorFieldRecompile = "一些检查器字段更改需要 Unity 重新编译。根据设置自动重新编译 Unity。";
|
||||
InfoDefaultProjectGeneration = "使用默认项目生成。如果您遇到 Unity 默认项目生成的任何问题,请考虑禁用它以使用自定义项目生成。";
|
||||
ErrorFreeChargesUnavailable = "免费费用不可用。如果问题仍然存在,请联系支持。";
|
||||
|
||||
// Exception Messages
|
||||
ExceptionExpectedZipFile = "预期以 .zip 结尾,但它是:{0}";
|
||||
ExceptionZipFileNotFound = "未找到 zip 文件 {0}";
|
||||
ExceptionUnzipFailed = "解压缩失败,退出代码 {0}";
|
||||
ExceptionDownloadFailed = "下载失败,状态码 {0},原因 {1}";
|
||||
ExceptionUnableToFindManifest = "[{0}] 无法找到 {1}";
|
||||
ErrorRedeemRequestFailed = "兑换请求失败。状态码:{0},原因:{1}";
|
||||
ErrorFailedDeserializingRedeem = "反序列化兑换响应失败,异常:{0}:{1}";
|
||||
ErrorRedeemingWebException = "兑换许可证失败:遇到 WebException {0}";
|
||||
ExceptionDownloadContentLengthUnknown = "下载失败:内容长度未知";
|
||||
ExceptionDownloadFileCorrupted = "下载失败:下载文件已损坏";
|
||||
ExceptionFailedToFindAppDirectory = "未能找到 .app 目录并将其移动到 {0}";
|
||||
ExceptionCouldNotStartCodePatcher = "无法启动代码修补程序进程。";
|
||||
|
||||
// Info/Debug Messages
|
||||
InfoManifestSearch = "在 {1} 未找到 {0},正在 {2} 内搜索清单文件";
|
||||
InfoOmitProjectsForPlayerBuild = "要像 Player 构建一样编译 C# 文件,我们必须省略不属于所选 Player 构建的项目。";
|
||||
InfoSeparator = "---------";
|
||||
InfoOmittedEditorProject = "省略的编辑器/其他项目名为:{0}";
|
||||
InfoFoundProjectNamed = "找到名为 {0} 的项目";
|
||||
|
||||
// Project Generation Warnings
|
||||
WarningPostProcessorException = "后处理器“{0}”在调用 OnGeneratedCSProjectFilesThreaded 时引发异常:\n{1}";
|
||||
WarningPostProcessorFailedProject = "后处理器“{0}”在处理项目“{1}”时失败:\n{2}";
|
||||
WarningPostProcessorFailedSolution = "后处理器“{0}”在处理解决方案“{1}”时失败:\n{2}";
|
||||
WarningPostProcessorNoDefaultConstructor = "类型“{0}”应具有公共默认构造函数,但没有";
|
||||
WarningPostProcessorConstructorException = "调用“{0}”的默认构造函数时发生异常:\n{1}";
|
||||
WarningPostProcessorUnknownException = "尝试创建后处理器“{0}”时遇到未知异常:\n{1}";
|
||||
|
||||
// Parse Errors
|
||||
ErrorParseError = "{0} 解析错误:{1}";
|
||||
|
||||
// Android Manifest Comments
|
||||
CommentAndroidCleartextPermit = "[{0}] 添加了 android:usesCleartextTraffic=\"true\" 以允许连接到您机器上运行的 Hot Reload http 服务器。";
|
||||
CommentAndroidCleartextDevelopmentOnly = "[{0}] 此更改仅在 Unity 开发构建中发生。您可以在 Hot Reload 设置窗口中禁用此功能。";
|
||||
|
||||
// Debug Messages
|
||||
DebugDetouringMethodFailed = "Detouring {0} 方法失败。{1} {2}";
|
||||
|
||||
// Package Update Errors
|
||||
ErrorRequestFailedStatusCode = "请求失败,状态码:{0} {1}";
|
||||
ErrorInvalidPackageJson = "无效的 package.json";
|
||||
ErrorInvalidVersionInPackageJson = "package.json 中的版本无效:'{0}'";
|
||||
ErrorUnableToFindManifestJson = "无法找到 manifest.json";
|
||||
ErrorNoDependenciesInManifest = "在 manifest.json 中找不到依赖项对象";
|
||||
ErrorDependenciesNullInManifest = "manifest.json 中的依赖项对象为 null";
|
||||
ErrorNoDependenciesSpecified = "manifest.json 中未指定依赖项";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1ff9542af2fa0c4d8b9bfb9620e29a0
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,112 @@
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class License {
|
||||
// License & Authentication
|
||||
public static string TitleHotReloadLimited;
|
||||
public static string TitleHotReloadLicense;
|
||||
public static string MessageTrialLicense;
|
||||
public static string MessageIndieLicenseActive;
|
||||
public static string MessageBusinessLicenseActive;
|
||||
public static string MessageLicenseWillRenew;
|
||||
public static string MessagePromoCodeActivated;
|
||||
public static string PromoCodesTitle;
|
||||
|
||||
// License Error Descriptions
|
||||
public static string LicenseErrorDeviceInUse;
|
||||
public static string LicenseErrorDeviceBlacklisted;
|
||||
public static string LicenseErrorIncorrectClock;
|
||||
public static string LicenseErrorActivation;
|
||||
public static string LicenseErrorDeleted;
|
||||
public static string LicenseErrorDisabled;
|
||||
public static string LicenseErrorExpired;
|
||||
public static string LicenseErrorInactive;
|
||||
public static string LicenseErrorCorrupted;
|
||||
public static string LicenseErrorNetwork;
|
||||
public static string LicenseErrorTrialExpired;
|
||||
public static string LicenseErrorInvalidCredentials;
|
||||
public static string LicenseErrorIncompatible;
|
||||
public static string LicenseErrorDefault;
|
||||
public static string LicenseErrorAssetStorePro;
|
||||
public static string LicenseErrorUnityPlusIndie;
|
||||
|
||||
// License Button Labels
|
||||
public static string LicenseButtonGetLicense;
|
||||
public static string LicenseButtonContactSupport;
|
||||
public static string LicenseButtonUpgradeLicense;
|
||||
public static string LicenseButtonManageLicense;
|
||||
|
||||
public static void LoadEnglish() {
|
||||
// License & Authentication
|
||||
TitleHotReloadLimited = "Hot Reload Limited";
|
||||
TitleHotReloadLicense = "Hot Reload License";
|
||||
MessageTrialLicense = "Using Trial license, valid until {0}";
|
||||
MessageIndieLicenseActive = " Indie license active";
|
||||
MessageBusinessLicenseActive = " Business license active";
|
||||
MessageLicenseWillRenew = "License will renew on {0}.";
|
||||
MessagePromoCodeActivated = "Your promo code has been successfully activated. Free trial has been extended by 3 months.";
|
||||
PromoCodesTitle = "Promo Codes";
|
||||
|
||||
// License Error Descriptions
|
||||
LicenseErrorDeviceInUse = "Another device is using your license. Please reach out to customer support for assistance.";
|
||||
LicenseErrorDeviceBlacklisted = "You device has been blacklisted.";
|
||||
LicenseErrorIncorrectClock = "Your license is not working because your computer's clock is incorrect. Please set the clock to the correct time to restore your license.";
|
||||
LicenseErrorActivation = "An error has occured while activating your license. Please contact customer support for assistance.";
|
||||
LicenseErrorDeleted = "Your license has been deleted. Please contact customer support for assistance.";
|
||||
LicenseErrorDisabled = "Your license has been disabled. Please contact customer support for assistance.";
|
||||
LicenseErrorExpired = "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.";
|
||||
LicenseErrorInactive = "Your license is currenty inactive. Please login with your email/password to activate your license.";
|
||||
LicenseErrorCorrupted = "Your license file was damaged or corrupted. Please login with your email/password to refresh your license file.";
|
||||
LicenseErrorNetwork = "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists.";
|
||||
LicenseErrorTrialExpired = "Your trial has expired. Activate a license with unlimited usage or continue using the Free version. View available plans on our website.";
|
||||
LicenseErrorInvalidCredentials = "Incorrect email/password. You can find your initial password in the sign-up email.";
|
||||
LicenseErrorIncompatible = "Please upgrade your license to continue using hotreload with Unity Pro.";
|
||||
LicenseErrorDefault = "We apologize, an error happened while verifying your license. Please reach out to customer support for assistance.";
|
||||
LicenseErrorAssetStorePro = "Unity Pro/Enterprise users from company with your number of employees require a Business license. Please upgrade your license on our website.";
|
||||
LicenseErrorUnityPlusIndie = "Unity Plus users require an Indie license. Please upgrade your license on our website.";
|
||||
|
||||
// License Button Labels
|
||||
LicenseButtonGetLicense = "Get License";
|
||||
LicenseButtonContactSupport = "Contact Support";
|
||||
LicenseButtonUpgradeLicense = "Upgrade License";
|
||||
LicenseButtonManageLicense = "Manage License";
|
||||
}
|
||||
|
||||
public static void LoadSimplifiedChinese() {
|
||||
// License & Authentication
|
||||
TitleHotReloadLimited = "Hot Reload 有限";
|
||||
TitleHotReloadLicense = "Hot Reload 许可证";
|
||||
MessageTrialLicense = "正在使用试用许可证,有效期至 {0}";
|
||||
MessageIndieLicenseActive = " 独立开发者许可证已激活";
|
||||
MessageBusinessLicenseActive = " 商业许可证已激活";
|
||||
MessageLicenseWillRenew = "许可证将于 {0} 续订。";
|
||||
MessagePromoCodeActivated = "您的促销代码已成功激活。免费试用期已延长3个月。";
|
||||
PromoCodesTitle = "促销代码";
|
||||
|
||||
// License Error Descriptions
|
||||
LicenseErrorDeviceInUse = "另一台设备正在使用您的许可证。请联系客户支持寻求帮助。";
|
||||
LicenseErrorDeviceBlacklisted = "您的设备已被列入黑名单。";
|
||||
LicenseErrorIncorrectClock = "您的许可证无法工作,因为您的计算机时钟不正确。请将时钟设置为正确的时间以恢复您的许可证。";
|
||||
LicenseErrorActivation = "激活您的许可证时发生错误。请联系客户支持寻求帮助。";
|
||||
LicenseErrorDeleted = "您的许可证已被删除。请联系客户支持寻求帮助。";
|
||||
LicenseErrorDisabled = "您的许可证已被禁用。请联系客户支持寻求帮助。";
|
||||
LicenseErrorExpired = "您的许可证已过期。请使用下方的“升级许可证”按钮续订您的许可证订阅,并使用您的电子邮件/密码登录以激活您的许可证。";
|
||||
LicenseErrorInactive = "您的许可证当前未激活。请使用您的电子邮件/密码登录以激活您的许可证。";
|
||||
LicenseErrorCorrupted = "您的许可证文件已损坏或损坏。请使用您的电子邮件/密码登录以刷新您的许可证文件。";
|
||||
LicenseErrorNetwork = "连接到我们的服务器时出现问题。请检查您的网络连接,如果问题仍然存在,请联系客户支持。";
|
||||
LicenseErrorTrialExpired = "您的试用已过期。激活具有无限使用权的许可证或继续使用免费版本。在我们网站上查看可用计划。";
|
||||
LicenseErrorInvalidCredentials = "电子邮件/密码不正确。您可以在注册电子邮件中找到您的初始密码。";
|
||||
LicenseErrorIncompatible = "请升级您的许可证以继续在 Unity Pro 中使用 hotreload。";
|
||||
LicenseErrorDefault = "我们深表歉意,验证您的许可证时发生错误。请联系客户支持寻求帮助。";
|
||||
LicenseErrorAssetStorePro = "拥有您员工数量的公司中的 Unity Pro/Enterprise 用户需要商业许可证。请在我们的网站上升级您的许可证。";
|
||||
LicenseErrorUnityPlusIndie = "Unity Plus 用户需要独立开发者许可证。请在我们的网站上升级您的许可证。";
|
||||
|
||||
// License Button Labels
|
||||
LicenseButtonGetLicense = "获取许可证";
|
||||
LicenseButtonContactSupport = "联系支持";
|
||||
LicenseButtonUpgradeLicense = "升级许可证";
|
||||
LicenseButtonManageLicense = "管理许可证";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35d12621e71ef12458b3bbe48e047469
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,61 @@
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
[InitializeOnLoad]
|
||||
internal static partial class Translations {
|
||||
static string loadedLocale;
|
||||
static Translations() {
|
||||
LoadDefaultLocalization();
|
||||
}
|
||||
|
||||
public static void LoadDefaultLocalization() {
|
||||
LoadLocalization(PackageConst.DefaultLocale);
|
||||
}
|
||||
|
||||
static void LoadLocalization(string locale) {
|
||||
if (loadedLocale == locale) {
|
||||
return;
|
||||
}
|
||||
if (locale == Locale.SimplifiedChinese) {
|
||||
LoadSimplifiedChinese();
|
||||
} else {
|
||||
LoadEnglish();
|
||||
}
|
||||
loadedLocale = locale;
|
||||
}
|
||||
|
||||
static void LoadEnglish() {
|
||||
Common.LoadEnglish();
|
||||
Timeline.LoadEnglish();
|
||||
License.LoadEnglish();
|
||||
Errors.LoadEnglish();
|
||||
Registration.LoadEnglish();
|
||||
Dialogs.LoadEnglish();
|
||||
Settings.LoadEnglish();
|
||||
OnDevice.LoadEnglish();
|
||||
About.LoadEnglish();
|
||||
Miscellaneous.LoadEnglish();
|
||||
Suggestions.LoadEnglish();
|
||||
Utility.LoadEnglish();
|
||||
UI.LoadEnglish();
|
||||
}
|
||||
|
||||
static void LoadSimplifiedChinese() {
|
||||
Common.LoadSimplifiedChinese();
|
||||
Timeline.LoadSimplifiedChinese();
|
||||
License.LoadSimplifiedChinese();
|
||||
Errors.LoadSimplifiedChinese();
|
||||
Registration.LoadSimplifiedChinese();
|
||||
Dialogs.LoadSimplifiedChinese();
|
||||
Settings.LoadSimplifiedChinese();
|
||||
OnDevice.LoadSimplifiedChinese();
|
||||
About.LoadSimplifiedChinese();
|
||||
Miscellaneous.LoadSimplifiedChinese();
|
||||
Suggestions.LoadSimplifiedChinese();
|
||||
Utility.LoadSimplifiedChinese();
|
||||
UI.LoadSimplifiedChinese();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e39c1f768bb465fa48c1fe2c3dd4d75
|
||||
timeCreated: 1759652321
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,13 @@
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor.Localization {
|
||||
internal static partial class Translations {
|
||||
public static class MenuItems {
|
||||
public const string OpenHotReload = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "Window/Hot Reload/打开 &#H" : "Window/Hot Reload/Open &#H";
|
||||
public const string RecompileHotReload = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "Window/Hot Reload/重新编译" : "Window/Hot Reload/Recompile";
|
||||
public const string OverlayDescription = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "Hot Reload" : "Hot Reload";
|
||||
public const string NotImplementedObsolete = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "未实现" : "Not implemented";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff31c47e148cfc243a9f2c173a058c3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs
|
||||
uploadId: 870414
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user