diff --git a/Assets/Scripts/Dice/DiceRoller.cs b/Assets/Scripts/Dice/DiceRoller.cs index 887210c..e0fc295 100644 --- a/Assets/Scripts/Dice/DiceRoller.cs +++ b/Assets/Scripts/Dice/DiceRoller.cs @@ -147,6 +147,6 @@ public sealed class DiceRoller : MonoBehaviour IsRolling = false; OnRollFinished?.Invoke(topValue); - Debug.Log($"{gameObject.name} | Выпало: {topValue}"); + // Debug.Log($"{gameObject.name} | Выпало: {topValue}"); } } diff --git a/Packages/com.singularitygroup.hotreload/Editor.meta b/Packages/com.singularitygroup.hotreload/Editor.meta new file mode 100644 index 0000000..74ca4b0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f5dfa6492e8e7ce4f937aa75ef4e86fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution.meta b/Packages/com.singularitygroup.hotreload/Editor/Attribution.meta new file mode 100644 index 0000000..944cb63 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ae8b0adf00c450d9e80e11ffa1d2cf7 +timeCreated: 1678721517 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs new file mode 100644 index 0000000..1d0ab63 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta new file mode 100644 index 0000000..a0d7e00 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs new file mode 100644 index 0000000..1cd7459 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs @@ -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 + )); + } + } + + /// + /// Registers and attempts to send a Verified Solutions Attribution event. + /// + /// Name of the action, identifying a place this event was called from. + /// Identifiable Verified Solutions Partner's name. + /// Unique identifier of the customer using Partner's Verified Solution. + 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; + } + + /// + /// Registers and attempts to send a Verified Solutions Attribution event. + /// + /// Name of the action, identifying a place this event was called from. + /// Identifiable Verified Solutions Partner's name. + /// Unique identifier of the customer using Partner's Verified Solution. + 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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta new file mode 100644 index 0000000..d5b0f71 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI.meta new file mode 100644 index 0000000..7ce99ac --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a100625513d043c7bb875461043f4f86 +timeCreated: 1673820086 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs new file mode 100644 index 0000000..5febc3e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs.meta new file mode 100644 index 0000000..85107ca --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs new file mode 100644 index 0000000..6601490 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs @@ -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; + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs.meta new file mode 100644 index 0000000..9327bc9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs new file mode 100644 index 0000000..82795f5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs @@ -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; + + /// + /// Public API: Starts the Hot Reload server. Must be on the main thread + /// + 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); + } + } + + /// + /// Public API: Stops the Hot Reload server + /// + /// + /// This is a no-op in case the server is not running + /// + 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(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 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)); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs.meta new file mode 100644 index 0000000..1393d62 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs new file mode 100644 index 0000000..93d8826 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs @@ -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(); + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs.meta new file mode 100644 index 0000000..126d695 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs new file mode 100644 index 0000000..cbdb5c9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs @@ -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; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs.meta new file mode 100644 index 0000000..c3f87f1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs new file mode 100644 index 0000000..1c922bd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs @@ -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 MacOSVersion = new Lazy(() => { + //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}"); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs.meta new file mode 100644 index 0000000..f82272e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs new file mode 100644 index 0000000..7d6b2a5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs @@ -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; + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs.meta new file mode 100644 index 0000000..5983554 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs b/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs new file mode 100644 index 0000000..d623fda --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs.meta new file mode 100644 index 0000000..497fc52 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker.meta b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker.meta new file mode 100644 index 0000000..855fa44 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 80c2056f805745542a2c295385b25479 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs new file mode 100644 index 0000000..f623b59 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs @@ -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 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs.meta new file mode 100644 index 0000000..9daea4e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs new file mode 100644 index 0000000..8569d65 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs @@ -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 + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs.meta new file mode 100644 index 0000000..5e2259c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs new file mode 100644 index 0000000..e1ffeef --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs @@ -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 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs.meta new file mode 100644 index 0000000..dfecd05 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Constants.cs b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs new file mode 100644 index 0000000..30e1815 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs @@ -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; + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Constants.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs.meta new file mode 100644 index 0000000..6ad33f2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Demo.meta b/Packages/com.singularitygroup.hotreload/Editor/Demo.meta new file mode 100644 index 0000000..1c88280 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Demo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c5c2596a7a469c42a1a6b35017d8a49 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs b/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs new file mode 100644 index 0000000..895c685 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs @@ -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 + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs.meta new file mode 100644 index 0000000..bd57d6b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs new file mode 100644 index 0000000..6c0f0aa --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs @@ -0,0 +1,1561 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.Editor.Demo; +using SingularityGroup.HotReload.EditorDependencies; +using UnityEditor; +using UnityEngine; +using Debug = UnityEngine.Debug; +using Task = System.Threading.Tasks.Task; +using System.Reflection; +using System.Runtime.CompilerServices; +using SingularityGroup.HotReload.Editor.Localization; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor.Build; +using UnityEditor.Compilation; +using UnityEditor.UIElements; +using UnityEditorInternal; +using UnityEngine.UIElements; + +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.IntegrationTests")] + +namespace SingularityGroup.HotReload.Editor { + internal class Config { + public bool patchEditModeOnlyOnEditorFocus; + public string[] assetBlacklist; + public bool changePlaymodeTint; + public bool disableCompilingFromEditorScripts; + public bool enableInspectorFreezeFix; + } + + [InitializeOnLoad] + internal static class EditorCodePatcher { + static string sessionFilePath = PackageConst.LibraryCachePath + "/sessionId.txt"; + + internal static readonly ServerDownloader serverDownloader; + internal static bool _compileError; + internal static bool _applyingFailed; + internal static bool _appliedPartially; + internal static bool _appliedUndetected; + + static Timer timer; + static bool init; + + internal static UnityLicenseType licenseType { get; private set; } + internal static bool LoginNotRequired => PackageConst.IsAssetStoreBuild && licenseType != UnityLicenseType.UnityPro; + internal static bool compileError => _compileError; + + internal static PatchStatus patchStatus = PatchStatus.None; + + internal static event Action<(MethodPatchResponse, RegisterPatchesResult)> OnPatchHandled; + + internal static Config config; + + + #if ODIN_INSPECTOR + internal static bool DrawPrefix(Sirenix.OdinInspector.Editor.InspectorProperty __instance) { + return !UnityFieldHelper.IsFieldHidden(__instance.ParentType, __instance.Name); + } + internal static MethodInfo OdinPropertyDrawPrefixInfo = typeof(EditorCodePatcher).GetMethod("DrawPrefix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + #if UNITY_2021_1_OR_NEWER + internal static MethodInfo OdinPropertyDrawInfo = typeof(Sirenix.OdinInspector.Editor.InspectorProperty)?.GetMethod("Draw", 0, BindingFlags.Instance | BindingFlags.Public, null, new Type[]{}, null); + #else + internal static MethodInfo OdinPropertyDrawInfo = typeof(Sirenix.OdinInspector.Editor.InspectorProperty)?.GetMethod("Draw", BindingFlags.Instance | BindingFlags.Public, null, new Type[]{}, null); + #endif + internal static MethodInfo DrawOdinInspectorInfo = typeof(Sirenix.OdinInspector.Editor.OdinEditor)?.GetMethod("DrawOdinInspector", BindingFlags.NonPublic | BindingFlags.Instance); + #else + internal static MethodInfo OdinPropertyDrawPrefixInfo = null; + internal static MethodInfo OdinPropertyDrawInfo = null; + internal static MethodInfo DrawOdinInspectorInfo = null; + #endif + + internal static MethodInfo GetDrawVInspectorInfo() { + // performance optimization + if (!Directory.Exists("Assets/vInspector")) { + return null; + } + try { + var t = Type.GetType("VInspector.AbstractEditor, VInspector"); + return t?.GetMethod("OnInspectorGUI", BindingFlags.Public | BindingFlags.Instance); + } catch { + // ignore + } + return null; + } + + internal static ICompileChecker compileChecker; + static bool quitting; + static EditorCodePatcher() { + if(init) { + //Avoid infinite recursion in case the static constructor gets accessed via `InitPatchesBlocked` below + return; + } + Translations.LoadDefaultLocalization(); + SingularityGroup.HotReload.Localization.Translations.LoadDefaultLocalization(); + if (File.Exists(PackageConst.ConfigFilePath)) { + config = JsonConvert.DeserializeObject(File.ReadAllText(PackageConst.ConfigFilePath)); + } else { + config = new Config(); + } + init = true; + UnityHelper.Init(); + //Use synchonization context if possible because it's more reliable. + ThreadUtility.InitEditor(); + if (!EditorWindowHelper.IsHumanControllingUs()) { + return; + } + + serverDownloader = new ServerDownloader(); + serverDownloader.CheckIfDownloaded(HotReloadCli.controller); + SingularityGroup.HotReload.Demo.Demo.I = new EditorDemo(); + if (HotReloadPrefs.DeactivateHotReload) { + ResetSettings(); + return; + } + + // ReSharper disable ExpressionIsAlwaysNull + UnityFieldHelper.Init(Log.Warning, HotReloadRunTab.Recompile, DrawOdinInspectorInfo, OdinPropertyDrawInfo, OdinPropertyDrawPrefixInfo, GetDrawVInspectorInfo(), typeof(UnityFieldDrawerPatchHelper), typeof(VisualElement)); + + timer = new Timer(OnIntervalThreaded, (Action) OnIntervalMainThread, 500, 500); + + if (MultiplayerPlaymodeHelper.IsClone) { + InitServerInfo(); + } else { + UpdateHost(); + } + licenseType = UnityLicenseHelper.GetLicenseType(); + compileChecker = CompileChecker.Create(); + compileChecker.onCompilationFinished += OnCompilationFinished; + EditorApplication.delayCall += InstallUtility.CheckForNewInstall; + AddEditorFocusChangedHandler(OnEditorFocusChanged); + // When domain reloads, this is a good time to ensure server has up-to-date project information + if (ServerHealthCheck.I.IsServerHealthy) { + EditorApplication.delayCall += TryPrepareBuildInfo; + } + HotReloadSuggestionsHelper.Init(); + // reset in case last session didn't shut down properly + CheckEditorSettings(); + EditorApplication.quitting += ResetSettingsOnQuit; + + AssemblyReloadEvents.beforeAssemblyReload += () => { + HotReloadTimelineHelper.PersistTimeline().Forget(); + }; + + ServerHealthCheck.instance.CheckHealth(); + if (ServerHealthCheck.I.IsServerHealthy && !HotReloadPrefs.AutoClearTimeline) { + HotReloadTimelineHelper.InitPersistedEvents().Forget(); + } else { + HotReloadTimelineHelper.ClearPersistance(); + } + + CompilationPipeline.assemblyCompilationFinished += (string _, CompilerMessage[] messages) => { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + foreach (var message in messages) { + if (message.type != CompilerMessageType.Error) { + continue; + } + if (!message.message.Contains("Sirenix")) { + continue; + } + if (message.message.Contains("CS0012") + || message.message.Contains("CS0234") + || message.message.Contains("CS0246") + || message.message.Contains("CS9286") + ) { + #if UNITY_2021_1_OR_NEWER + var target = NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup); + var symbols = PlayerSettings.GetScriptingDefineSymbols(target).Split(";").ToList(); + symbols.Remove("ODIN_INSPECTOR"); + PlayerSettings.SetScriptingDefineSymbols(target, string.Join(";", symbols)); + #else + var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup).Split(';').ToList(); + symbols.Remove("ODIN_INSPECTOR"); + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, string.Join(";", symbols)); + #endif + } + } + }; + + CompilationPipeline.compilationFinished += obj => { + if (MultiplayerPlaymodeHelper.IsClone) { + CompileMethodDetourer.Reset(); + return; + } + // reset in case package got removed + // if it got removed, it will not be enabled again + // if it wasn't removed, settings will get handled by OnIntervalMainThread + AutoRefreshSettingChecker.Reset(); + ScriptCompilationSettingChecker.Reset(); + PlaymodeTintSettingChecker.Reset(); + HotReloadRunTab.recompiling = false; + CompileMethodDetourer.Reset(); + + if (!HotReloadPrefs.AutoClearTimeline) { + HotReloadTimelineHelper.CreateReloadFinishedEventEntry(patchedMethodsDisplayNames: new string[]{"Full assembly recompilation"}); + } + }; + DetectEditorStart(); + DetectVersionUpdate(); + CodePatcher.I.fieldHandler = new FieldHandler(FieldDrawerUtil.StoreField, UnityFieldHelper.HideField, UnityFieldHelper.RegisterInspectorFieldAttributes); + if (EditorApplication.isPlayingOrWillChangePlaymode) { + CodePatcher.I.InitPatchesBlocked(); + } + +#pragma warning disable CS0612 // Type or member is obsolete + if (HotReloadPrefs.RateAppShownLegacy) { + HotReloadPrefs.RateAppShown = true; + } + if (!File.Exists(HotReloadPrefs.showOnStartupPath)) { + var showOnStartupLegacy = HotReloadPrefs.GetShowOnStartupEnum(); + HotReloadPrefs.ShowOnStartup = showOnStartupLegacy; + } +#pragma warning restore CS0612 // Type or member is obsolete + + HotReloadState.ShowingRedDot = false; + + if (DateTime.Now < new DateTime(2023, 11, 1)) { + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023); + } else { + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023); + } + + EditorApplication.playModeStateChanged += state => { + if (state == PlayModeStateChange.EnteredEditMode && HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode) { + if (TryRecompileUnsupportedChanges()) { + HotReloadState.RecompiledUnsupportedChangesOnExitPlaymode = true; + } + } + }; + if (HotReloadState.RecompiledUnsupportedChangesInPlaymode) { + HotReloadState.RecompiledUnsupportedChangesInPlaymode = false; + EditorApplication.isPlaying = true; + } +#if UNITY_2020_1_OR_NEWER + if (CompilationPipeline.codeOptimization != CodeOptimization.Release) { + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods); + } +#endif + if (!HotReloadState.EditorCodePatcherInit) { + ClearPersistence(); + HotReloadState.EditorCodePatcherInit = true; + } + + CodePatcher.I.debuggerCompatibilityEnabled = !HotReloadPrefs.AutoDisableHotReloadWithDebugger; + } + + static void ResetSettingsOnQuit() { + quitting = true; + ResetSettings(); + } + + static void ResetSettings() { + AutoRefreshSettingChecker.Reset(); + ScriptCompilationSettingChecker.Reset(); + PlaymodeTintSettingChecker.Reset(); + HotReloadCli.StopAsync().Forget(); + CompileMethodDetourer.Reset(); + } + + public static bool autoRecompileUnsupportedChangesSupported; + static void AddEditorFocusChangedHandler(Action handler) { + var eventInfo = typeof(EditorApplication).GetEvent("focusChanged", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + var addMethod = eventInfo?.GetAddMethod(true) ?? eventInfo?.GetAddMethod(false); + if (addMethod != null) { + addMethod.Invoke(null, new object[]{ handler }); + } + autoRecompileUnsupportedChangesSupported = addMethod != null; + } + + private static void OnEditorFocusChanged(bool hasFocus) { + if (hasFocus && !HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately) { + TryRecompileUnsupportedChanges(); + } + } + + public static bool TryRecompileUnsupportedChanges() { + if (MultiplayerPlaymodeHelper.IsClone) { + return false; + } + var isPlaying = EditorApplication.isPlaying; + + var hasPartiallyUnsupportedPatches = false; + foreach (var patchResponse in CodePatcher.I.PatchHistory) { + if (patchResponse.partiallySupportedChanges == null) { + continue; + } + hasPartiallyUnsupportedPatches |= HasPartiallySupportedChangesFiltered(patchResponse.partiallySupportedChanges); + } + + if (!HotReloadPrefs.AutoRecompileUnsupportedChanges + || !CodePatcher.I.anyFailures + && (!HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges || !hasPartiallyUnsupportedPatches) + || _compileError + || isPlaying && !HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode + || !isPlaying && !HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode + ) { + return false; + } + RecompileUnsupportedChanges(); + return true; + } + + public static void RecompileUnsupportedChanges() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + if (HotReloadPrefs.ShowCompilingUnsupportedNotifications) { + EditorWindowHelper.ShowNotification(EditorWindowHelper.NotificationStatus.NeedsRecompile); + } + if (EditorApplication.isPlaying) { + HotReloadState.RecompiledUnsupportedChangesInPlaymode = true; + } + HotReloadRunTab.Recompile(); + } + + private static DateTime lastPrepareBuildInfo = DateTime.UtcNow; + + /// Post state for player builds. + /// Only check build target because user can change build settings whenever. + internal static void TryPrepareBuildInfo() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + // Note: we post files state even when build target is wrong + // because you might connect with a build downloaded onto the device. + if ((DateTime.UtcNow - lastPrepareBuildInfo).TotalSeconds > 5) { + lastPrepareBuildInfo = DateTime.UtcNow; + HotReloadCli.PrepareBuildInfoAsync().Forget(); + } + } + + internal static void RecordActiveDaysForRateApp() { + var unixDay = (int)(DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 86400); + var activeDays = GetActiveDaysForRateApp(); + if (activeDays.Count < Constants.DaysToRateApp && activeDays.Add(unixDay.ToString())) { + HotReloadPrefs.ActiveDays = string.Join(",", activeDays); + } + } + + internal static HashSet GetActiveDaysForRateApp() { + if (string.IsNullOrEmpty(HotReloadPrefs.ActiveDays)) { + return new HashSet(); + } + return new HashSet(HotReloadPrefs.ActiveDays.Split(',')); + } + + // CheckEditorStart distinguishes between domain reload and first editor open + // We have some separate logic on editor start (InstallUtility.HandleEditorStart) + private static void DetectEditorStart() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + var editorId = EditorAnalyticsSessionInfo.id; + var currVersion = PackageConst.Version; + Task.Run(() => { + try { + var lines = File.Exists(sessionFilePath) ? File.ReadAllLines(sessionFilePath) : Array.Empty(); + + long prevSessionId = -1; + string prevVersion = null; + if (lines.Length >= 2) { + long.TryParse(lines[1], out prevSessionId); + } + if (lines.Length >= 3) { + prevVersion = lines[2].Trim(); + } + var updatedFromVersion = (prevSessionId != -1 && currVersion != prevVersion) ? prevVersion : null; + + if (prevSessionId != editorId && prevSessionId != 0) { + // back to mainthread + ThreadUtility.RunOnMainThread(() => { + InstallUtility.HandleEditorStart(updatedFromVersion); + + var newEditorId = EditorAnalyticsSessionInfo.id; + if (newEditorId != 0) { + Task.Run(() => { + try { + // editorId isn't available on first domain reload, must do it here + File.WriteAllLines(sessionFilePath, new[] { + "1", // serialization version + newEditorId.ToString(), + currVersion, + }); + + } catch (IOException) { + // ignore + } + }); + } + }); + } + + } catch (IOException) { + // ignore + } catch (Exception e) { + ThreadUtility.LogException(e); + } + }); + } + + private static void DetectVersionUpdate() { + if (serverDownloader.CheckIfDownloaded(HotReloadCli.controller) || MultiplayerPlaymodeHelper.IsClone) { + return; + } + ServerHealthCheck.instance.CheckHealth(); + if (!ServerHealthCheck.I.IsServerHealthy) { + return; + } + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRestartServer, + Translations.Dialogs.DialogMessageRestartUpdate, + Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + RestartCodePatcher().Forget(); + } + } + + private static void UpdateHost() { + RequestHelper.SetServerInfo(new PatchServerInfo(RequestHelper.defaultServerHost, HotReloadState.ServerPort, null, Path.GetFullPath("."))); + } + + static void OnIntervalThreaded(object o) { + var wasHealhy = ServerHealthCheck.I.IsServerHealthy; + ServerHealthCheck.instance.CheckHealth(); + if (wasHealhy != ServerHealthCheck.I.IsServerHealthy) { + InitServerInfo(); + } + if (MultiplayerPlaymodeHelper.IsClone && ServerHealthCheck.I.IsServerHealthy) { + // technically need to call this once but for consistency sake we call it every time (overhead should be minimal) + RequestHelper.RegisterClone().Forget(); + } + ThreadUtility.RunOnMainThread((Action)o); + if (serverDownloader.Progress >= 1f) { + serverDownloader.CheckIfDownloaded(HotReloadCli.controller); + } + } + + private static bool _requestingFlushErrors; + private static long _lastErrorFlush; + private static async Task RequestFlushErrors() { + _requestingFlushErrors = true; + try { + await RequestFlushErrorsCore(); + } finally { + _requestingFlushErrors = false; + } + } + + private static async Task RequestFlushErrorsCore() { + var pollFrequency = 500; + // Delay until we've hit the poll request frequency + var waitMs = (int)Mathf.Clamp(pollFrequency - ((DateTime.Now.Ticks / (float)TimeSpan.TicksPerMillisecond) - _lastErrorFlush), 0, pollFrequency); + await Task.Delay(waitMs); + await FlushErrors(); + _lastErrorFlush = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + } + + public static bool disableServerLogs; + public static string lastCompileErrorLog; + static async Task FlushErrors() { + var response = await RequestHelper.RequestFlushErrors(); + if (response == null || disableServerLogs || MultiplayerPlaymodeHelper.IsClone) { + return; + } + if (!Application.isPlaying && HotReloadPrefs.PauseHotReloadInEditMode) { + return; + } + if (Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled) { + return; + } + foreach (var responseWarning in response.warnings) { + if (responseWarning.Contains("Scripts have compile errors")) { + if (compileError) { + Log.Error(responseWarning); + } else { + lastCompileErrorLog = responseWarning; + } + } else { + Log.Warning(responseWarning); + } + + if (responseWarning.Contains("Multidimensional arrays are not supported")) { + await ThreadUtility.SwitchToMainThread(); + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.MultidimensionalArrays); + } + } + foreach (var responseError in response.errors) { + Log.Error(responseError); + } + } + + internal static bool firstPatchAttempted; + internal static bool loggedDebuggerRecompile; + static void OnIntervalMainThread() { + HotReloadSuggestionsHelper.Check(); + + // Moved from RequestServerInfo to avoid GC allocations when HR is not active + + // Repaint if the running Status has changed since the layout changes quite a bit + if (running != ServerHealthCheck.I.IsServerHealthy) { + if (HotReloadWindow.Current) { + HotReloadRunTab.RepaintInstant(); + } + running = ServerHealthCheck.I.IsServerHealthy; + } + if (!running) { + startupCompletedAt = null; + } + if (!running && !StartedServerRecently()) { + // Reset startup progress + startupProgress = null; + } + + if (!ServerHealthCheck.I.IsServerHealthy) { + stopping = false; + } + if (startupProgress?.Item1 == 1) { + starting = false; + } + if (!_requestingFlushErrors && Running) { + RequestFlushErrors().Forget(); + } + + if (!Application.isPlaying && HotReloadPrefs.PauseHotReloadInEditMode) { + return; + } + if (Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled) { + var userAutoRefreshDisabled = AutoRefreshSettingChecker.IsUserAutoRefreshDisabled(); + if (!HotReloadPrefs.DebuggerOnboardingShown) { + HotReloadPrefs.DebuggerOnboardingShown = true; + if (EditorUtility.DisplayDialogComplex( + title: Translations.Dialogs.DialogTitleHotReloadDebuggerDetected, + message: userAutoRefreshDisabled ? Translations.Dialogs.DialogMessageHotReloadDebuggerDetectedPause : Translations.Dialogs.DialogMessageHotReloadDebuggerDetectedRecompile, + ok: userAutoRefreshDisabled ? Translations.Dialogs.DialogButtonHotReloadDebuggerDetectedPause : Translations.Dialogs.DialogButtonHotReloadDebuggerDetectedRecompile, + cancel: Translations.Dialogs.DialogCloseHotReloadDebuggerDetected, + alt: Translations.Dialogs.DialogButtonHotReloadDebuggerDetectedAdvancedOptions) == 2 + ) { + if (EditorUtility.DisplayDialog( + title: Translations.Dialogs.DialogTitleHotReloadDebuggerOptions, + message: Translations.Dialogs.DialogMessageHotReloadDebuggerOptions, + ok: Translations.Dialogs.DialogButtonHotReloadDebuggerOptionsContinue, + cancel: userAutoRefreshDisabled ? Translations.Dialogs.DialogButtonHotReloadDebuggerOptionsCancelPause : Translations.Dialogs.DialogButtonHotReloadDebuggerOptionsCancelRecompile) + ) { + CodePatcher.I.debuggerCompatibilityEnabled = true; + HotReloadPrefs.AutoDisableHotReloadWithDebugger = false; + return; + } + } + } + if (!HotReloadState.WarnedDebuggerAttached) { + HotReloadState.WarnedDebuggerAttached = true; + // passed both prompts - hot reload is paused + if (CodePatcher.I.PatchesApplied > 0 || !userAutoRefreshDisabled) { + Log.Info(Translations.Errors.InfoDebuggerAttachedFullRecompile); + } else { + // warn about Hot Reload being paused + Log.Info(Translations.Errors.InfoDebuggerAttachedPauseHotReload); + } + } + if (CodePatcher.I.PatchesApplied > 0) { + // recompile if any patches were made to avoid debugger session being broken + HotReloadRunTab.Recompile(); + } + return; + } else { + HotReloadState.WarnedDebuggerAttached = false; + } + + if(ServerHealthCheck.I.IsServerHealthy) { + // NOTE: avoid calling this method when HR is not running to avoid allocations + RequestServerInfo(); + TryPrepareBuildInfo(); + if (!requestingCompile && (!config.patchEditModeOnlyOnEditorFocus || Application.isPlaying || UnityEditorInternal.InternalEditorUtility.isApplicationActive)) { + RequestHelper.PollMethodPatches(HotReloadState.LastPatchId, resp => HandleResponseReceived(resp)); + } + RequestHelper.PollPatchStatus(resp => { + patchStatus = resp.patchStatus; + if (patchStatus == PatchStatus.Compiling) { + startWaitingForCompile = null; + } + if (patchStatus == PatchStatus.Patching) { + firstPatchAttempted = true; + if (HotReloadPrefs.ShowPatchingNotifications) { + EditorWindowHelper.ShowNotification(EditorWindowHelper.NotificationStatus.Patching, maxDuration: 10); + } + } else if (HotReloadPrefs.ShowPatchingNotifications) { + EditorWindowHelper.RemoveNotification(); + } + }, patchStatus); + if (HotReloadPrefs.AllAssetChanges) { + RequestHelper.PollAssetChanges(HandleAssetChange); + } +#if UNITY_2020_1_OR_NEWER + if (!disableInlineChecks) { + CheckInlinedMethods(); + } +#endif + } + CheckEditorSettings(); + } + +#if UNITY_2020_1_OR_NEWER + //only disabled for integration tests + internal static bool disableInlineChecks = false; + internal static HashSet inlinedMethodsFound = new HashSet(); + internal static void CheckInlinedMethods() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + if (CompilationPipeline.codeOptimization != CodeOptimization.Release) { + return; + } + HashSet newInlinedMethods = null; + try { + foreach (var method in CodePatcher.I.OriginalPatchMethods) { + if (inlinedMethodsFound.Contains(method)) { + continue; + } + var isMethodSynthesized = method.Name.Contains("<") || method.DeclaringType?.Name.Contains("<") == true && method.Name == ".ctor"; + if (!(method is ConstructorInfo) && !isMethodSynthesized && MethodUtils.IsMethodInlined(method)) { + if (newInlinedMethods == null) { + newInlinedMethods = new HashSet(); + } + newInlinedMethods.Add(method); + } + } + if (newInlinedMethods?.Count > 0) { + if (!HotReloadPrefs.LoggedInlinedMethodsDialogue) { + Log.Warning(Translations.Errors.WarningInlinedMethods); + HotReloadPrefs.LoggedInlinedMethodsDialogue = true; + } + HotReloadTimelineHelper.CreateInlinedMethodsEntry(entryType: EntryType.Foldout, patchedMethodsDisplayNames: newInlinedMethods.Select(mb => $"{mb.DeclaringType?.Name}::{mb.Name}").ToArray()); + CodePatcher.I.anyFailures = true; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + TryRecompileUnsupportedChanges(); + } + HotReloadSuggestionsHelper.SetSuggestionActive(HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods); + foreach (var newInlinedMethod in newInlinedMethods) { + inlinedMethodsFound.Add(newInlinedMethod); + } + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Patching, StatEventType.Inlined)).Forget(); + } + } catch (Exception e) { + Log.Warning(Translations.Errors.WarningInlineMethodChecker, e.Message); + } + } +#endif + + static void CheckEditorSettings() { + if (quitting) { + return; + } + CheckAutoRefresh(); + CheckScriptCompilation(); + CheckPlaymodeTint(); + CheckAssetDatabaseRefresh(); + } + + static void CheckAutoRefresh() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + var disabledDuringDebugger = Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled; + if (HotReloadPrefs.AllowDisableUnityAutoRefresh && ServerHealthCheck.I.IsServerHealthy && !disabledDuringDebugger) { + AutoRefreshSettingChecker.Apply(); + AutoRefreshSettingChecker.Check(); + } else { + AutoRefreshSettingChecker.Reset(); + } + } + + static void CheckScriptCompilation() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + var disabledDuringDebugger = Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled; + if (HotReloadPrefs.AllowDisableUnityAutoRefresh && ServerHealthCheck.I.IsServerHealthy && !disabledDuringDebugger) { + ScriptCompilationSettingChecker.Apply(); + ScriptCompilationSettingChecker.Check(); + } else { + ScriptCompilationSettingChecker.Reset(); + } + } + + static string[] assetExtensionBlacklist = new[] { + ".cs", + // we can add setting to allow scenes to get hot reloaded for users who collaborate (their scenes change externally) + ".unity", + // safer to ignore meta files completely until there's a use-case + ".meta", + // debug files + ".mdb", + ".pdb", + ".compute", + // ".shader", //use assetBlacklist instead + }; + + public static string[] compileFiles = new[] { + ".asmdef", + ".asmref", + ".rsp", + }; + + public static string[] plugins = new[] { + // native plugins + ".dll", + ".bundle", + ".dylib", + ".so", + // plugin scripts + ".cpp", + ".h", + ".aar", + ".jar", + ".a", + ".java" + }; + + static void HandleAssetChange(string assetPath) { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + // ignore directories + if (Directory.Exists(assetPath)) { + return; + } + // ignore temp compile files + if (assetPath.Contains("UnityDirMonSyncFile") + || assetPath.EndsWith("~", StringComparison.Ordinal) + || assetPath.Contains("StreamingAssets") + ) { + return; + } + foreach (var compileFile in compileFiles) { + if (assetPath.EndsWith(compileFile, StringComparison.Ordinal)) { + HotReloadTimelineHelper.CreateErrorEventEntry(string.Format(Translations.Utility.AssemblyFileEditError, assetPath), entryType: EntryType.Foldout); + CodePatcher.I.anyFailures = true; + _applyingFailed = true; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + TryRecompileUnsupportedChanges(); + } + return; + } + } + // Add plugin changes to unsupported changes list + foreach (var plugin in plugins) { + if (assetPath.EndsWith(plugin, StringComparison.Ordinal)) { + HotReloadTimelineHelper.CreateErrorEventEntry(string.Format(Translations.Utility.NativePluginEditError, assetPath), entryType: EntryType.Foldout); + CodePatcher.I.anyFailures = true; + _applyingFailed = true; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + TryRecompileUnsupportedChanges(); + } + return; + } + } + + // ignore file extensions that trigger domain reload + if (!HotReloadPrefs.IncludeShaderChanges) { + if (assetPath.EndsWith(".shader", StringComparison.Ordinal)) { + return; + } + } + foreach (var blacklisted in assetExtensionBlacklist) { + if (assetPath.EndsWith(blacklisted, StringComparison.Ordinal)) { + return; + } + } + if (config?.assetBlacklist != null) { + foreach (var blacklisted in config.assetBlacklist) { + if (assetPath.EndsWith(blacklisted, StringComparison.Ordinal)) { + return; + } + } + } + var path = ToPath(assetPath); + if (path == null) { + return; + } + try { + if (!File.Exists(assetPath)) { + AssetDatabase.DeleteAsset(path); + } else { + AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); + } + } catch (Exception e){ + Log.Warning(Translations.Errors.WarningRefreshingAssetFailed, assetPath, e); + } + } + + static string ToPath(string assetPath) { + var relativePath = GetRelativePath(assetPath, Path.GetFullPath("Assets")); + var relativePathPackages = GetRelativePath(assetPath, Path.GetFullPath("Packages")); + // ignore files outside assets and packages folders + if (relativePath.StartsWith("..", StringComparison.Ordinal)) { + relativePath = null; + } + if (relativePathPackages.StartsWith("..", StringComparison.Ordinal)) { + relativePathPackages = null; + #if UNITY_2021_1_OR_NEWER + // Might be inside a package "file:" + try { + foreach (var package in UnityEditor.PackageManager.PackageInfo.GetAllRegisteredPackages()) { + if (assetPath.StartsWith(package.resolvedPath.Replace("\\", "/"), StringComparison.Ordinal)) { + relativePathPackages = $"Packages/{package.name}/{assetPath.Substring(package.resolvedPath.Length)}"; + break; + } + } + } catch { + // ignore + } + #endif + } + return relativePath ?? relativePathPackages; + } + + public static string GetRelativePath(string filespec, string folder) { + Uri pathUri = new Uri(filespec); + Uri folderUri = new Uri(folder); + return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); + } + + static void CheckPlaymodeTint() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + if (config.changePlaymodeTint && ServerHealthCheck.I.IsServerHealthy && Application.isPlaying) { + PlaymodeTintSettingChecker.Apply(); + PlaymodeTintSettingChecker.Check(); + } else { + PlaymodeTintSettingChecker.Reset(); + } + } + + static void CheckAssetDatabaseRefresh() { + if (config.disableCompilingFromEditorScripts && ServerHealthCheck.I.IsServerHealthy) { + CompileMethodDetourer.Apply(); + } else { + CompileMethodDetourer.Reset(); + } + } + + static void HandleResponseReceived(MethodPatchResponse response) { + RegisterPatchesResult patchResult = null; + if (response.patches?.Length > 0 + || response.alteredFields.Length > 0 + || response.removedFieldInitializers.Length > 0 + || response.addedFieldInitializerInitializers.Length > 0 + || response.addedFieldInitializerFields.Length > 0 + ) { + LogBurstHint(response); + // don't save patches in virtual players since we will use main editor instance for that + var persist = !MultiplayerPlaymodeHelper.IsClone; + patchResult = CodePatcher.I.RegisterPatches(response, persist: persist); + } + + CodePatcher.I.RegisterFailures(response, patchResult); + + if (patchResult?.inspectorModified == true) { + // repaint all views calls all gui callbacks but doesn't rebuild the visual tree + // which is needed to hide removed fields + UnityFieldDrawerPatchHelper.repaintVisualTree = true; + InternalEditorUtility.RepaintAllViews(); + } + + // Keep in sync with HasPartiallySupportedChangesFiltered + var partiallySupportedChangesFiltered = new List(response.partiallySupportedChanges ?? Array.Empty()); + partiallySupportedChangesFiltered.RemoveAll(x => !HotReloadTimelineHelper.GetPartiallySupportedChangePref(x)); + if (!HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported && partiallySupportedChangesFiltered.Remove(PartiallySupportedChange.AddMonobehaviourMethod)) { + if (HotReloadSuggestionsHelper.CanShowServerSuggestion(HotReloadSuggestionKind.AddMonobehaviourMethod)) { + HotReloadSuggestionsHelper.SetServerSuggestionShown(HotReloadSuggestionKind.AddMonobehaviourMethod); + } + } + var failuresDeduplicated = new HashSet(response.failures ?? Array.Empty()); + + foreach (var hotReloadSuggestionKind in response.suggestions) { + if (HotReloadSuggestionsHelper.CanShowServerSuggestion(hotReloadSuggestionKind)) { + HotReloadSuggestionsHelper.SetServerSuggestionShown(hotReloadSuggestionKind); + } + } + + var allMethods = patchResult?.patchedSMethods.Select(m => GetExtendedMethodName(m)); + if (allMethods == null) { + allMethods = response.removedMethod?.Select(m => GetExtendedMethodName(m)).Distinct(StringComparer.OrdinalIgnoreCase) ?? Array.Empty(); + } else { + allMethods = allMethods.Concat(response.removedMethod?.Select(m => GetExtendedMethodName(m)) ?? Array.Empty()).Distinct(StringComparer.OrdinalIgnoreCase); + } + + var allFields = (patchResult?.addedFields.Select(f => GetExtendedFieldName(f)) ?? Array.Empty()) + .Concat(response.alteredFields?.Select(f => GetExtendedFieldName(f)).Distinct(StringComparer.OrdinalIgnoreCase) ?? Array.Empty()) + .Concat(response.patches?.SelectMany(p => p?.propertyAttributesFieldUpdated ?? Array.Empty()).Select(f => GetExtendedFieldName(f)).Distinct(StringComparer.OrdinalIgnoreCase) ?? Array.Empty()) + .Distinct(StringComparer.OrdinalIgnoreCase); + + var patchedMembersDisplayNames = allMethods.Concat(allFields).ToArray(); + + _compileError = response.failures?.Any(failure => failure.Contains("error CS")) ?? false; + _applyingFailed = response.failures?.Length > 0 || patchResult?.patchFailures.Count > 0 || patchResult?.patchExceptions.Count > 0; + _appliedPartially = !_applyingFailed && partiallySupportedChangesFiltered.Count > 0; + _appliedUndetected = patchedMembersDisplayNames.Length == 0; + + if (!_compileError) { + lastCompileErrorLog = null; + } + + var autoRecompiled = false; + if (_compileError) { + HotReloadTimelineHelper.EventsTimeline.RemoveAll(e => e.alertType == AlertType.CompileError); + foreach (var failure in failuresDeduplicated) { + if (failure.Contains("error CS")) { + HotReloadTimelineHelper.CreateErrorEventEntry(failure); + } + } + if (lastCompileErrorLog != null) { + if (!disableServerLogs) { + Log.Error(lastCompileErrorLog); + } + lastCompileErrorLog = null; + } + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Reload, StatEventType.CompileError), new EditorExtraData { + { StatKey.PatchId, response.id }, + }).Forget(); + } else if (_applyingFailed) { + if (partiallySupportedChangesFiltered.Count > 0) { + foreach (var responsePartiallySupportedChange in partiallySupportedChangesFiltered) { + HotReloadTimelineHelper.CreatePartiallyAppliedEventEntry(responsePartiallySupportedChange, entryType: EntryType.Child); + } + } + foreach (var failure in failuresDeduplicated) { + HotReloadTimelineHelper.CreateErrorEventEntry(failure, entryType: EntryType.Child); + } + if (patchResult?.patchFailures.Count > 0) { + foreach (var failure in patchResult.patchFailures) { + SMethod method = failure.Item1; + string error = failure.Item2; + HotReloadTimelineHelper.CreatePatchFailureEventEntry(error, methodName: GetMethodName(method), methodSimpleName: method.simpleName, entryType: EntryType.Child); + } + } + if (patchResult?.patchExceptions.Count > 0) { + foreach (var error in patchResult.patchExceptions) { + HotReloadTimelineHelper.CreateErrorEventEntry(error, entryType: EntryType.Child); + } + } + HotReloadTimelineHelper.CreateReloadFinishedWithWarningsEventEntry(patchedMembersDisplayNames: patchedMembersDisplayNames); + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.UnsupportedChanges); + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + autoRecompiled = TryRecompileUnsupportedChanges(); + } + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Reload, StatEventType.Failure), new EditorExtraData { + { StatKey.PatchId, response.id }, + }).Forget(); + } else if (_appliedPartially) { + foreach (var responsePartiallySupportedChange in partiallySupportedChangesFiltered) { + HotReloadTimelineHelper.CreatePartiallyAppliedEventEntry(responsePartiallySupportedChange, entryType: EntryType.Child, detailed: false); + } + HotReloadTimelineHelper.CreateReloadPartiallyAppliedEventEntry(patchedMethodsDisplayNames: patchedMembersDisplayNames); + + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + autoRecompiled = TryRecompileUnsupportedChanges(); + } + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Reload, StatEventType.Partial), new EditorExtraData { + { StatKey.PatchId, response.id }, + }).Forget(); + } else if (_appliedUndetected) { + HotReloadTimelineHelper.CreateReloadUndetectedChangeEventEntry(); + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Reload, StatEventType.Undetected), new EditorExtraData { + { StatKey.PatchId, response.id }, + }).Forget(); + } else { + HotReloadTimelineHelper.CreateReloadFinishedEventEntry(patchedMethodsDisplayNames: patchedMembersDisplayNames); + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Reload, StatEventType.Finished), new EditorExtraData { + { StatKey.PatchId, response.id }, + }).Forget(); + } + + if (!autoRecompiled && patchResult?.inspectorFieldAdded == true && HotReloadPrefs.AutoRecompileInspectorFieldsEdit && !Application.isPlaying) { + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.UnsupportedChanges); + RecompileUnsupportedChanges(); + autoRecompiled = true; + HotReloadTimelineHelper.CreateErrorEventEntry(Translations.Utility.InspectorFieldChangeError, entryType: EntryType.Child); + HotReloadTimelineHelper.CreateReloadFinishedWithWarningsEventEntry(); + Log.Info(Translations.Errors.InfoInspectorFieldRecompile); + } + + // When patching different assembly, compile error will get removed, even though it's still there + // It's a shortcut we take for simplicity + if (!_compileError) { + HotReloadTimelineHelper.EventsTimeline.RemoveAll(x => x.alertType == AlertType.CompileError); + } + + foreach (string responseFailure in response.failures) { + if (responseFailure.Contains("error CS") && !disableServerLogs) { + Log.Error(responseFailure); + } else if (autoRecompiled) { + Log.Info(responseFailure); + } else { + Log.Warning(responseFailure); + } + } + if (patchResult?.patchFailures.Count > 0) { + foreach (var patchResultPatchFailure in patchResult.patchFailures) { + if (autoRecompiled) { + Log.Info(patchResultPatchFailure.Item2); + } else { + Log.Warning(patchResultPatchFailure.Item2); + } + } + } + if (patchResult?.patchExceptions.Count > 0) { + foreach (var patchResultPatchException in patchResult.patchExceptions) { + if (autoRecompiled) { + Log.Info(patchResultPatchException); + } else { + Log.Warning(patchResultPatchException); + } + } + } + + // attempt to recompile if previous Unity compilation had compilation errors + // because new changes might've fixed those errors + if (compileChecker.hasCompileErrors) { + HotReloadRunTab.Recompile(); + } + + if (HotReloadWindow.Current) { + HotReloadWindow.Current.Repaint(); + } + HotReloadState.LastPatchId = response.id; + OnPatchHandled?.Invoke((response, patchResult)); + } + + // Keep in sync with HandleResponseReceived + static bool HasPartiallySupportedChangesFiltered(PartiallySupportedChange[] partiallySupportedChanges) { + foreach (var change in partiallySupportedChanges) { + if (HotReloadTimelineHelper.GetPartiallySupportedChangePref(change) && + (change != PartiallySupportedChange.AddMonobehaviourMethod || HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported) + ) { + return true; + } + } + return false; + } + + static string GetExtendedMethodName(SMethod method) { + var colonIndex = method.displayName.IndexOf("::", StringComparison.Ordinal); + if (colonIndex > 0) { + var beforeColon = method.displayName.Substring(0, colonIndex); + var spaceIndex = beforeColon.LastIndexOf(".", StringComparison.Ordinal); + if (spaceIndex > 0) { + var className = beforeColon.Substring(spaceIndex + 1); + return className + "::" + method.simpleName; + } + } + return method.simpleName; + } + + static string GetExtendedFieldName(SField field) { + string typeName = field.declaringType.typeName; + var simpleTypeIndex = typeName.LastIndexOf(".", StringComparison.Ordinal); + if (simpleTypeIndex > 0) { + typeName = typeName.Substring(simpleTypeIndex + 1); + } + return $"{typeName}::{field.fieldName}"; + } + + static string GetMethodName(SMethod method) { + var spaceIndex = method.displayName.IndexOf(" ", StringComparison.Ordinal); + if (spaceIndex > 0) { + return method.displayName.Substring(spaceIndex); + } + return method.displayName; + } + + + [Conditional("UNITY_2022_2_OR_NEWER")] + static void LogBurstHint(MethodPatchResponse response) { + if(HotReloadPrefs.LoggedBurstHint) { + return; + } + foreach (var patch in response.patches) { + if(patch.unityJobs.Length > 0) { + Debug.LogWarning(string.Format(Translations.Errors.WarningUnityJobHotReloaded, Constants.TroubleshootingURL)); + HotReloadPrefs.LoggedBurstHint = true; + break; + } + } + } + + private static DateTime? startWaitingForCompile; + static void OnCompilationFinished() { + ServerHealthCheck.instance.CheckHealth(); + if(ServerHealthCheck.I.IsServerHealthy) { + startWaitingForCompile = DateTime.UtcNow; + firstPatchAttempted = false; + RequestCompile().Forget(); + } + ClearPersistence(); + } + + static void ClearPersistence() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + CodePatcher.I.ClearPatchesThreaded(); + } + + static bool requestingCompile; + static async Task RequestCompile() { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + requestingCompile = true; + try { + await RequestHelper.RequestClearPatches(); + await ProjectGeneration.ProjectGeneration.GenerateSlnAndCsprojFiles(Application.dataPath); + await RequestHelper.RequestCompile(scenePath => { + var path = ToPath(scenePath); + if (File.Exists(scenePath) && path != null) { + AssetDatabase.ImportAsset(path, ImportAssetOptions.Default); + } + }); + } finally { + requestingCompile = false; + } + } + + private static bool stopping; + private static bool starting; + private static DateTime? startupCompletedAt; + private static Tuple startupProgress; + + internal static bool Started => ServerHealthCheck.I.IsServerHealthy && DownloadProgress == 1 && StartupProgress?.Item1 == 1; + internal static bool Starting => (StartedServerRecently() || ServerHealthCheck.I.IsServerHealthy) && !Started && starting && patchStatus != PatchStatus.CompileError; + internal static bool Stopping => stopping && Running; + internal static bool Compiling => DateTime.UtcNow - startWaitingForCompile < TimeSpan.FromSeconds(5) || patchStatus == PatchStatus.Compiling || HotReloadRunTab.recompiling; + internal static Tuple StartupProgress => startupProgress; + + + /// + /// We have a button to stop the Hot Reload server.
+ /// Store task to ensure only one stop attempt at a time. + ///
+ private static DateTime? serverStartedAt; + private static DateTime? serverStoppedAt; + private static DateTime? serverRestartedAt; + private static bool StartedServerRecently() { + return DateTime.UtcNow - serverStartedAt < ServerHealthCheck.HeartBeatTimeout; + } + + internal static bool StoppedServerRecently() { + return DateTime.UtcNow - serverStoppedAt < ServerHealthCheck.HeartBeatTimeout || (!StartedServerRecently() && (startupProgress?.Item1 ?? 0) == 0); + } + + internal static bool RestartedServerRecently() { + return DateTime.UtcNow - serverRestartedAt < ServerHealthCheck.HeartBeatTimeout; + } + + private static bool requestingStart; + private static async Task StartCodePatcher(LoginData loginData = null) { + if (requestingStart || StartedServerRecently()) { + return; + } + stopping = false; + starting = true; + var exposeToNetwork = HotReloadPrefs.ExposeServerToLocalNetwork; + var allAssetChanges = HotReloadPrefs.AllAssetChanges; + var disableConsoleWindow = HotReloadPrefs.DisableConsoleWindow; + var isReleaseMode = RequestHelper.IsReleaseMode(); + var detailedErrorReporting = !HotReloadPrefs.DisableDetailedErrorReporting; +#if UNITY_EDITOR_WIN + var useWatchman = HotReloadPrefs.UseWatchman; +#endif + CodePatcher.I.ClearPatchedMethods(); + RecordActiveDaysForRateApp(); + try { + requestingStart = true; + startupProgress = Tuple.Create(0f, Translations.UI.StartingHotReloadMessage); + serverStartedAt = DateTime.UtcNow; + await HotReloadCli.StartAsync( + exposeToNetwork, + allAssetChanges, + disableConsoleWindow, + isReleaseMode, + detailedErrorReporting, +#if UNITY_EDITOR_WIN + useWatchman, +#endif + loginData + ).ConfigureAwait(false); + } + catch (Exception ex) { + ThreadUtility.LogException(ex); + } + finally { + requestingStart = false; + } + } + + private static bool requestingStop; + internal static async Task StopCodePatcher(bool recompileOnDone = false) { + stopping = true; + starting = false; + if (requestingStop) { + if (recompileOnDone) { + await ThreadUtility.SwitchToMainThread(); + HotReloadRunTab.Recompile(); + } + return; + } + CodePatcher.I.ClearPatchedMethods(); + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning); + try { + requestingStop = true; + await HotReloadCli.StopAsync().ConfigureAwait(false); + serverStoppedAt = DateTime.UtcNow; + await ThreadUtility.SwitchToMainThread(); + if (recompileOnDone) { + HotReloadRunTab.Recompile(); + } + startupProgress = null; + } + catch (Exception ex) { + ThreadUtility.LogException(ex); + } + finally { + requestingStop = false; + } + } + + private static bool requestingRestart; + internal static async Task RestartCodePatcher() { + if (requestingRestart) { + return; + } + try { + requestingRestart = true; + await StopCodePatcher(); + await DownloadAndRun(); + serverRestartedAt = DateTime.UtcNow; + } + finally { + requestingRestart = false; + } + } + + + private static bool requestingDownloadAndRun; + internal static float DownloadProgress => serverDownloader.Progress; + internal static bool DownloadRequired => DownloadProgress < 1f; + internal static bool DownloadStarted => serverDownloader.Started; + internal static bool RequestingDownloadAndRun => requestingDownloadAndRun; + internal static CancellationTokenSource downloadCancelToken; + + internal static async Task DownloadAndRun(LoginData loginData = null, bool recompileOnDone = false) { + if (requestingDownloadAndRun) { + return false; + } + stopping = false; + requestingDownloadAndRun = true; + try { + if (DownloadRequired) { + downloadCancelToken = new CancellationTokenSource(); + var ok = await serverDownloader.PromptForDownload(downloadCancelToken.Token); + if (!ok) { + return false; + } + } + await StartCodePatcher(loginData); + await ThreadUtility.SwitchToMainThread(); + if (HotReloadPrefs.DeactivateHotReload) { + HotReloadPrefs.DeactivateHotReload = false; + HotReloadRunTab.Recompile(); + } + return true; + } finally { + requestingDownloadAndRun = false; + } + } + + private static void InitServerInfo() { + // only needed for clones + if (!MultiplayerPlaymodeHelper.IsClone) { + return; + } + var serverInfoRaw = File.ReadAllText(PackageConst.ServerInfoFilePath); + PatchServerInfo serverInfo = null; + if (!string.IsNullOrEmpty(serverInfoRaw)) { + serverInfo = JsonConvert.DeserializeObject(serverInfoRaw); + } + if (serverInfo != null) { + RequestHelper.SetServerInfo(serverInfo); + } + } + + private const int SERVER_POLL_FREQUENCY_ON_STARTUP_MS = 500; + private const int SERVER_POLL_FREQUENCY_AFTER_STARTUP_MS = 2000; + private static int GetPollFrequency() { + return (startupProgress != null && startupProgress.Item1 < 1) || StartedServerRecently() + ? SERVER_POLL_FREQUENCY_ON_STARTUP_MS + : SERVER_POLL_FREQUENCY_AFTER_STARTUP_MS; + } + + internal static bool RequestingLoginInfo { get; set; } + + [CanBeNull] internal static LoginStatusResponse Status { get; private set; } + internal static void HandleStatus(LoginStatusResponse resp) { + if (resp == null) { + return; + } + Attribution.RegisterLogin(resp); + + bool consumptionsChanged = Status?.freeSessionRunning != resp.freeSessionRunning || Status?.freeSessionEndTime != resp.freeSessionEndTime; + bool expiresAtChanged = Status?.licenseExpiresAt != resp.licenseExpiresAt; + if (!EditorCodePatcher.LoginNotRequired + && !resp.isLicensed + && resp.consumptionsUnavailableReason == ConsumptionsUnavailableReason.UnrecoverableError + && Status?.consumptionsUnavailableReason != ConsumptionsUnavailableReason.UnrecoverableError + ) { + Log.Error(Translations.Errors.ErrorFreeChargesUnavailable); + } + if (!RequestingLoginInfo && resp.requestError == null) { + Status = resp; + } + if (resp.lastLicenseError == null) { + // If we got success, we should always show an error next time it comes up + HotReloadPrefs.ErrorHidden = false; + } + + var oldStartupProgress = startupProgress; + var newStartupProgress = Tuple.Create( + resp.startupProgress, + string.IsNullOrEmpty(resp.startupStatus) ? Translations.UI.StartingHotReloadMessage : resp.startupStatus); + + startupProgress = newStartupProgress; + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (startupCompletedAt == null && newStartupProgress.Item1 == 1f) { + startupCompletedAt = DateTime.UtcNow; + } + + if (oldStartupProgress == null + || Math.Abs(oldStartupProgress.Item1 - newStartupProgress.Item1) > 0 + || oldStartupProgress.Item2 != newStartupProgress.Item2 + || consumptionsChanged + || expiresAtChanged + ) { + // Send project files state now that server can receive requests (only needed for player builds) + TryPrepareBuildInfo(); + } + } + + internal static async Task RequestLogin(string email, string password) { + RequestingLoginInfo = true; + try { + int i = 0; + while (!Running && i < 100) { + await Task.Delay(100); + i++; + } + + Status = await RequestHelper.RequestLogin(email, password, 10); + + // set to false so new error is shown + HotReloadPrefs.ErrorHidden = false; + if (Status?.isLicensed == true) { + HotReloadPrefs.LicenseEmail = email; + HotReloadPrefs.LicensePassword = Status.initialPassword ?? password; + } + } finally { + RequestingLoginInfo = false; + } + } + private static bool requestingServerInfo; + private static long lastServerPoll; + private static bool running; + internal static bool Running => ServerHealthCheck.I.IsServerHealthy; + + internal static void RequestServerInfo() { + if (requestingServerInfo || MultiplayerPlaymodeHelper.IsClone) { + return; + } + RequestServerInfoAsync().Forget(); + } + + private static async Task RequestServerInfoAsync() { + requestingServerInfo = true; + try { + await RequestServerInfoCore(); + } finally { + requestingServerInfo = false; + } + } + + private static async Task RequestServerInfoCore() { + var pollFrequency = GetPollFrequency(); + // Delay until we've hit the poll request frequency + var waitMs = (int)Mathf.Clamp(pollFrequency - ((DateTime.Now.Ticks / (float)TimeSpan.TicksPerMillisecond) - lastServerPoll), 0, pollFrequency); + await Task.Delay(waitMs); + + if (!ServerHealthCheck.I.IsServerHealthy) { + return; + } + + + var resp = await RequestHelper.GetLoginStatus(30); + HandleStatus(resp); + + lastServerPoll = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + } + } + + // IMPORTANT: don't change the names of the methods + internal static class UnityFieldDrawerPatchHelper { + internal static void PatchCustom(Rect contentRect, UnityEditor.Editor __instance) { + if (__instance.target) { + FieldDrawerUtil.DrawFromObject(__instance.target); + } + } + + internal static void PatchDefault(UnityEditor.Editor __instance) { + if (__instance.target) { + FieldDrawerUtil.DrawFromObject(__instance.target); + } + } + + internal static bool repaintVisualTree; + internal static void PatchFillDefaultInspector(VisualElement container, SerializedObject serializedObject, UnityEditor.Editor editor) { + HideChildren(container, serializedObject); + if (editor.target) { + var child = new IMGUIContainer((() => + { + FieldDrawerUtil.DrawFromObject(editor.target); + if (repaintVisualTree) { + HideChildren(container, serializedObject); + ResetInvalidatedInspectorFields(container, serializedObject); + // Mark dirty to repaint the visual tree + container.MarkDirtyRepaint(); + repaintVisualTree = false; + } + })); + child.name = "SingularityGroup.HotReload.FieldDrawer"; + container.Add(child); + } + } + + static List childrenToRemove = new List(); + static void HideChildren(VisualElement container, SerializedObject serializedObject) { + if (container == null) { + return; + } + childrenToRemove.Clear(); + foreach (var child in container.Children()) { + if (!(child is PropertyField propertyField)) { + continue; + } + try { + if (serializedObject != null && serializedObject.targetObject && UnityFieldHelper.IsFieldHidden(serializedObject.targetObject.GetType(), serializedObject.FindProperty(propertyField.bindingPath)?.name ?? "")) { + childrenToRemove.Add(child); + } + } catch (NullReferenceException) { + // serializedObject.targetObject throws nullref in cases where e.g. exising playmode + } + } + foreach (var child in childrenToRemove) { + container.Remove(child); + } + childrenToRemove.Clear(); + } + + static void ResetInvalidatedInspectorFields(VisualElement container, SerializedObject serializedObject) { + if (container == null || serializedObject == null) { + return; + } + foreach (var child in container.Children()) { + if (!(child is PropertyField propertyField)) { + continue; + } + try { + var prop = serializedObject.FindProperty(propertyField.bindingPath); + if (prop != null && serializedObject.targetObject && UnityFieldHelper.HasFieldInspectorCacheInvalidation(serializedObject.targetObject.GetType(), prop.name ?? "")) { + child.GetType().GetMethod("Reset", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerializedProperty) }, null)?.Invoke(child, new object[] { prop }); + } + } catch (NullReferenceException) { + // serializedObject.targetObject throws nullref in cases where e.g. exising playmode + } + } + } + + internal static bool GetHandlerPrefix( + SerializedProperty property, + ref object __result + ) { + if (property == null || property.serializedObject == null || !property.serializedObject.targetObject) { + // do nothing + return true; + } + if (UnityFieldHelper.TryInvalidateFieldInspectorCache(property.serializedObject.targetObject.GetType(), property.name)) { + __result = null; + return false; + } + return true; + } + + internal static bool GetFieldAttributesPrefix( + FieldInfo field, + ref List __result + ) { + if (field == null) { + // do nothing + return true; + } + List result; + if (UnityFieldHelper.TryGetInspectorFieldAttributes(field, out result)) { + __result = result; + return false; + } + return true; + } + + internal static bool PropertyFieldPrefix( + Rect position, + UnityEditor.SerializedProperty property, + GUIContent label, + bool includeChildren, + Rect visibleArea, + ref bool __result + ) { + if (property == null || property.serializedObject == null || !property.serializedObject.targetObject) { + // do nothing + return true; + } + if (UnityFieldHelper.IsFieldHidden(property.serializedObject.targetObject.GetType(), property.name)) { + // make sure field doesn't take any space + __result = false; + return false; // Skip original method + } + return true; // Continue with original method + } + + internal static bool GetHightPrefix( + UnityEditor.SerializedProperty property, GUIContent label, bool includeChildren, + ref float __result + ) { + if (property == null || property.serializedObject == null || !property.serializedObject.targetObject) { + // do nothing + return true; + } + if (UnityFieldHelper.IsFieldHidden(property.serializedObject.targetObject.GetType(), property.name)) { + // make sure field doesn't take any space + __result = 0.0f; + return false; // Skip original method + } + return true; // Continue with original method + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs.meta new file mode 100644 index 0000000..1494126 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs new file mode 100644 index 0000000..ef70b00 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs @@ -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 IndicationIcon = new Dictionary { + // 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 IndicationText => new Dictionary { + { 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; + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs.meta new file mode 100644 index 0000000..1b72862 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs b/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs new file mode 100644 index 0000000..875381d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs @@ -0,0 +1,87 @@ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using Debug = UnityEngine.Debug; + +namespace SingularityGroup.HotReload.Editor { + internal static class GitUtil { + /// + /// Fallback is PatchServerInfo.UnknownCommitHash + /// + 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; + + /// + /// Get the git commit hash, returning null if it takes too long. + /// + /// + /// + /// + /// This method is 'better safe than sorry' because we must not break the user's build.
+ /// It is better to not know the commit hash than to fail the build. + ///
+ 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 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); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs.meta new file mode 100644 index 0000000..409a181 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers.meta new file mode 100644 index 0000000..9a003a6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 387b31d7da35b27428629a83bb4ac589 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs new file mode 100644 index 0000000..8a964e5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs @@ -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(toOmit); + return unique.OrderBy(s => s).ToArray(); + } + + // main thread only + public static List 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 { + /// + /// When define constraints evaluate to false, we need + /// + /// + /// + /// + /// Not aware of a Unity api to read defineConstraints, so we do it ourselves.
+ /// Find any asmdef files where the define constraints evaluate to false. + ///
+ public static string[] GetOmittedAssemblies(string[] defineSymbols) { + var guids = AssetDatabase.FindAssets("t:asmdef"); + var asmdefFiles = guids.Select(AssetDatabase.GUIDToAssetPath); + var shouldOmit = new List(); + 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(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 syntaxMap = new Dictionary { + { "OR", "||" }, + { "AND", "&&" }, + { "NOT", "!" } + }; + + + /// + /// Evaluate a define constraint like 'UNITY_ANDROID || UNITY_IOS' + /// + /// + /// + /// + 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, ""); + } + } + } + +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs.meta new file mode 100644 index 0000000..d415b2a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs new file mode 100644 index 0000000..3214681 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs @@ -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 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(defineSymbols); + defines.ExceptWith(editorSymbolsToRemove); + defines.UnionWith(androidSymbolsToAdd); + // sort for consistency, must be deterministic + var definesArray = defines.OrderBy(def => def).ToArray(); + return String.Join(";", definesArray); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs.meta new file mode 100644 index 0000000..760efac --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs new file mode 100644 index 0000000..5b0d6b8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs @@ -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!#$%&'*+/=?^_`{|}~]|(? notificationContent => new Dictionary { + { 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().ToArray(); + } + } + + private static EditorWindow[] sceneWindows { + get { + return Resources.FindObjectsOfTypeAll(typeof(SceneView)).Cast().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; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs.meta new file mode 100644 index 0000000..1975a03 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs new file mode 100644 index 0000000..fab85d7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs @@ -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 supportedInvertibleIcons = new Dictionary { + { 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 invertibleIconCache = new Dictionary(); + private static readonly Dictionary invertibleIconInvertedCache = new Dictionary(); + private static readonly Dictionary iconCache = new Dictionary(); + + 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(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("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(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 textureColorCache = new Dictionary(); + 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 grayTextureCache = new Dictionary(); + private static readonly Dictionary colorFactor = new Dictionary { + { "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; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs.meta new file mode 100644 index 0000000..9d9c8c5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs new file mode 100644 index 0000000..1f606ee --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs @@ -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 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 suggestionMap = new Dictionary { + { 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 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(); + 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; + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs.meta new file mode 100644 index 0000000..c6bf412 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs new file mode 100644 index 0000000..312e2cb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs @@ -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 eventsTimeline = new List(); + internal static List 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(File.ReadAllText(filePath))); + eventsTimeline = new List(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(); + } + + internal static readonly Dictionary alertIconString = new Dictionary { + { 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 partiallySupportedChangeDescriptions => new Dictionary { + {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 Suggestions = new List(); + 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 expandedEntries = new List(); + + 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[] 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); + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs.meta new file mode 100644 index 0000000..690f8c9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs new file mode 100644 index 0000000..5755fdb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs @@ -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); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs.meta new file mode 100644 index 0000000..86da73e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs new file mode 100644 index 0000000..b158a67 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs @@ -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()); + if (prefs != null) { + foreach (object kvp in prefs) { + var key = kvp.GetType().GetProperty("Key", BindingFlags.Instance | BindingFlags.Public)?.GetMethod.Invoke(kvp, Array.Empty()); + if (key?.ToString() == currentPlaymodeTintPrefKey) { + return kvp.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)?.GetMethod.Invoke(kvp, Array.Empty()); + } + + } + } + 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 }); + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs.meta new file mode 100644 index 0000000..94f9db1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs new file mode 100644 index 0000000..b99e2a6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs @@ -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 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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs.meta new file mode 100644 index 0000000..947fa06 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadAttributeProcessor.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs new file mode 100644 index 0000000..8798663 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs @@ -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().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; + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs.meta new file mode 100644 index 0000000..d31856e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs new file mode 100644 index 0000000..dcc6141 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs @@ -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().Show(); + EditorWindow.GetWindow().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 iconTextures = new Dictionary(); + 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; + /// + /// Create Hot Reload overlay panel. + /// + 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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs.meta new file mode 100644 index 0000000..c5f8a50 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs new file mode 100644 index 0000000..ad3ac99 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs @@ -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); } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs.meta new file mode 100644 index 0000000..8b69993 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs new file mode 100644 index 0000000..81a5de7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs @@ -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.editorAssetPath); + } else { + // create an instance with default values + return ScriptableObject.CreateInstance(); + } + } + + /// + /// Create settings asset file + /// + /// Assume that settings asset doesn't exist yet + /// The settings asset + 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(); + } + 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 + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs.meta new file mode 100644 index 0000000..ef67c1d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs new file mode 100644 index 0000000..59631f5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs @@ -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); } + } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs.meta new file mode 100644 index 0000000..4911190 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png b/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png new file mode 100644 index 0000000..ef9a138 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png.meta new file mode 100644 index 0000000..29ced5f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs b/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs new file mode 100644 index 0000000..330fbc1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs @@ -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; + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs.meta new file mode 100644 index 0000000..cb0d282 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation.meta new file mode 100644 index 0000000..09a0fe3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 12e88a0f97924d18859867b0cc957d03 +timeCreated: 1676802469 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs b/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs new file mode 100644 index 0000000..aceae28 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs @@ -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 DownloadFile(string url, string targetFilePath, IProgress 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 DownloadAsync(this HttpClient client, string requestUri, string destinationFilePath, IProgress 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(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 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) { + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs.meta new file mode 100644 index 0000000..28b214f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs b/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs new file mode 100644 index 0000000..9591972 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs @@ -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); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs.meta new file mode 100644 index 0000000..4007733 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs b/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs new file mode 100644 index 0000000..48bc621 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs @@ -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; + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs.meta new file mode 100644 index 0000000..0225cd6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs b/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs new file mode 100644 index 0000000..3f4e324 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs @@ -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 { + public float Progress {get; private set;} + public bool Started {get; private set;} + public int Attempts { get; private set; } + + class Config { + public Dictionary 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(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 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 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()); + 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(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(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.Report(float value) { + Progress = value; + } + + public async Task 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; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs.meta new file mode 100644 index 0000000..fefe5c3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs b/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs new file mode 100644 index 0000000..85fbbcc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs @@ -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 Update(string version, IProgress 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(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); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs.meta new file mode 100644 index 0000000..8bc2656 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization.meta new file mode 100644 index 0000000..13cbf1b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c856b3b3ce5f41c7aaebf7b543be697a +timeCreated: 1759652305 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs new file mode 100644 index 0000000..a4f82bb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs @@ -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 = "管理帐户"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs.meta new file mode 100644 index 0000000..d020307 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/AboutTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs new file mode 100644 index 0000000..111ce83 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs @@ -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 = "启动时显示"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs.meta new file mode 100644 index 0000000..a88930d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/CommonTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs new file mode 100644 index 0000000..5232956 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs @@ -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 = "使用完全重新编译"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs.meta new file mode 100644 index 0000000..3922cc3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/DialogTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs new file mode 100644 index 0000000..5d02105 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs @@ -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 中未指定依赖项"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs.meta new file mode 100644 index 0000000..9ecc6f7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/ErrorTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs new file mode 100644 index 0000000..0bf47e8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs @@ -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 = "管理许可证"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs.meta new file mode 100644 index 0000000..0901320 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/LicenseTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs new file mode 100644 index 0000000..cf24e90 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs @@ -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(); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs.meta new file mode 100644 index 0000000..40c430e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/Localization.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs new file mode 100644 index 0000000..aa921b8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs @@ -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"; + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs.meta new file mode 100644 index 0000000..9a3c67f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/MenuItemsTranslations.cs.meta @@ -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 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs new file mode 100644 index 0000000..8407af2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs @@ -0,0 +1,166 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Miscellaneous { + // Overlay/Toolbar + public static string OverlayTooltipRecompile; + + // Notifications + public static string NotificationPatching; + public static string NotificationNeedsRecompile; + + // Update Button + public static string ButtonUpdateToVersionFormat; + public static string ButtonTroubleshooting; + + // Rate App + public static string RateAppQuestion; + public static string RateAppThankYou; + + // Compilation Messages + public static string CompileErrorTapToSee; + public static string UnsupportedChangeTapToSee; + public static string TapToShowStacktrace; + + // Link Messages + public static string LinkForgotPassword; + + // Changelog + public static string ChangelogTitle; + + // Daily Session + public static string DailySessionStart; + public static string DailySessionTimeHoursLeft; + public static string DailySessionTimeMinutesLeft; + public static string DailySessionNextSessionMinutes; + public static string DailySessionNextSessionHours; + + // Indication Status Messages + public static string IndicationFinishRegistration; + public static string IndicationStarted; + public static string IndicationStopping; + public static string IndicationStopped; + public static string IndicationPaused; + public static string IndicationInstalling; + public static string IndicationStarting; + public static string IndicationReloaded; + public static string IndicationPartiallySupported; + public static string IndicationUnsupported; + public static string IndicationPatching; + public static string IndicationDisabledDuringDebugging; + public static string IndicationCompiling; + public static string IndicationCompileErrors; + public static string IndicationActivationFailed; + public static string IndicationLoading; + public static string IndicationUndetected; + + public static void LoadEnglish() { + // Overlay/Toolbar + OverlayTooltipRecompile = "Recompile"; + + // Notifications + NotificationPatching = "[Hot Reload] Applying patches..."; + NotificationNeedsRecompile = "[Hot Reload] Unsupported Changes detected! Recompiling..."; + + // Update Button + ButtonUpdateToVersionFormat = "Update To v{0}"; + ButtonTroubleshooting = "Troubleshooting"; + + // Rate App + RateAppQuestion = "Are you enjoying using Hot Reload?"; + RateAppThankYou = "Thank you for using Hot Reload!\n\nPlease consider leaving a review on the Asset Store to support us."; + + // Compilation Messages + CompileErrorTapToSee = "Compile error, tap here to see more."; + UnsupportedChangeTapToSee = "Unsupported change detected, tap here to see more."; + TapToShowStacktrace = "Tap to show stacktrace"; + + // Link Messages + LinkForgotPassword = "Forgot password?"; + + // Changelog + ChangelogTitle = "Changelog"; + + // Daily Session + DailySessionStart = "Daily Session: Make code changes to start"; + DailySessionTimeHoursLeft = "Daily Session: {0}h {1}m Left"; + DailySessionTimeMinutesLeft = "Daily Session: {0}m Left"; + DailySessionNextSessionMinutes = "Next Session: {0}m"; + DailySessionNextSessionHours = "Next Session: {0}h {1}m"; + + // Indication Status Messages + IndicationFinishRegistration = "Finish Registration"; + IndicationStarted = "Waiting for code changes"; + IndicationStopping = "Stopping Hot Reload"; + IndicationStopped = "Hot Reload inactive"; + IndicationPaused = "Hot Reload paused"; + IndicationInstalling = "Installing"; + IndicationStarting = "Starting Hot Reload"; + IndicationReloaded = "Reload finished"; + IndicationPartiallySupported = "Changes partially applied"; + IndicationUnsupported = "Finished with warnings"; + IndicationPatching = "Reloading"; + IndicationDisabledDuringDebugging = "Disabled during debugging"; + IndicationCompiling = "Compiling"; + IndicationCompileErrors = "Scripts have compile errors"; + IndicationActivationFailed = "Activation failed"; + IndicationLoading = "Loading"; + IndicationUndetected = "No changes applied"; + } + + public static void LoadSimplifiedChinese() { + // Overlay/Toolbar + OverlayTooltipRecompile = "重新编译"; + + // Notifications + NotificationPatching = "[Hot Reload] 正在应用补丁..."; + NotificationNeedsRecompile = "[Hot Reload] 检测到不支持的更改!正在重新编译..."; + + // Update Button + ButtonUpdateToVersionFormat = "更新到 v{0}"; + ButtonTroubleshooting = "疑难解答"; + + // Rate App + RateAppQuestion = "您喜欢使用 Hot Reload 吗?"; + RateAppThankYou = "感谢您使用 Hot Reload!\n\n请考虑在 Asset Store 上留下评论以支持我们。"; + + // Compilation Messages + CompileErrorTapToSee = "编译错误,点击此处查看更多。"; + UnsupportedChangeTapToSee = "检测到不支持的更改,点击此处查看更多。"; + TapToShowStacktrace = "点击以显示堆栈跟踪"; + + // Link Messages + LinkForgotPassword = "忘记密码?"; + + // Changelog + ChangelogTitle = "更新日志"; + + // Daily Session + DailySessionStart = "每日会话:进行代码更改以开始"; + DailySessionTimeHoursLeft = "每日会话:剩余 {0}h {1}m"; + DailySessionTimeMinutesLeft = "每日会话:剩余 {0}m"; + DailySessionNextSessionMinutes = "下一会话:{0}m"; + DailySessionNextSessionHours = "下一会话:{0}h {1}m"; + + // Indication Status Messages + IndicationFinishRegistration = "完成注册"; + IndicationStarted = "等待代码更改"; + IndicationStopping = "正在停止 Hot Reload"; + IndicationStopped = "Hot Reload 未激活"; + IndicationPaused = "Hot Reload 已暂停"; + IndicationInstalling = "正在安装"; + IndicationStarting = "正在启动 Hot Reload"; + IndicationReloaded = "重新加载完成"; + IndicationPartiallySupported = "更改部分应用"; + IndicationUnsupported = "完成但有警告"; + IndicationPatching = "正在重新加载"; + IndicationCompiling = "正在编译"; + IndicationCompileErrors = "脚本有编译错误"; + IndicationDisabledDuringDebugging = "调试期间已禁用"; + IndicationActivationFailed = "激活失败"; + IndicationLoading = "正在加载"; + IndicationUndetected = "未应用任何更改"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs.meta new file mode 100644 index 0000000..8a60563 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 839cc9c3fc3098d478a7450b4d23b907 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/MiscellaneousTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs new file mode 100644 index 0000000..ee21a0e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs @@ -0,0 +1,61 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class OnDevice { + // On-Device Settings + public static string OnDeviceHeadline; + public static string OnDeviceManualConnectFormat; + public static string OnDeviceManualConnectWithIP; + public static string OnDeviceNetworkNote; + public static string OnDeviceCheckHotReloadRunning; + public static string OnDeviceCheckHotReloadNotRunning; + public static string OnDeviceCheckEnableExposeServer; + public static string OnDeviceCheckPlatformSelected; + public static string OnDeviceCheckPlatformNotSupported; + public static string OnDeviceCheckDevelopmentEnabled; + public static string OnDeviceCheckEnableDevelopment; + public static string OnDeviceCheckMonoBackend; + public static string OnDeviceCheckSetMonoBackend; + public static string OnDeviceCheckStrippingLevel; + public static string OnDeviceCheckStrippingSolution; + + public static void LoadEnglish() { + // On-Device Settings + OnDeviceHeadline = "Make changes to a build running on-device"; + OnDeviceManualConnectFormat = "If auto-pair fails, find your local IP in OS settings, and use this format to connect: '{ip}:{0}'"; + OnDeviceManualConnectWithIP = "If auto-pair fails, use this IP and port to connect: {0}:{1}\nMake sure you are on the same LAN/WiFi network"; + OnDeviceNetworkNote = "Make sure you are on the same LAN/WiFi network"; + OnDeviceCheckHotReloadRunning = "Hot Reload is running"; + OnDeviceCheckHotReloadNotRunning = "Hot Reload is not running"; + OnDeviceCheckEnableExposeServer = "Enable '{0}'"; + OnDeviceCheckPlatformSelected = "The {0} platform is selected"; + OnDeviceCheckPlatformNotSupported = "The current platform is {0} which is not supported"; + OnDeviceCheckDevelopmentEnabled = "Development Build is enabled"; + OnDeviceCheckEnableDevelopment = "Enable \"Development Build\""; + OnDeviceCheckMonoBackend = "Scripting Backend is set to Mono"; + OnDeviceCheckSetMonoBackend = "Set Scripting Backend to Mono"; + OnDeviceCheckStrippingLevel = "Stripping Level = {0}"; + OnDeviceCheckStrippingSolution = "Code stripping needs to be disabled to ensure that all methods are available for patching."; + } + + public static void LoadSimplifiedChinese() { + // On-Device Settings + OnDeviceHeadline = "对在设备上运行的构建进行更改"; + OnDeviceManualConnectFormat = "如果自动配对失败,请在操作系统设置中找到您的本地 IP,并使用此格式进行连接:'{ip}:{0}'"; + OnDeviceManualConnectWithIP = "如果自动配对失败,请使用此 IP 和端口进行连接:{0}:{1}\n确保您在同一个局域网/WiFi 网络中"; + OnDeviceNetworkNote = "确保您在同一个局域网/WiFi 网络中"; + OnDeviceCheckHotReloadRunning = "Hot Reload 正在运行"; + OnDeviceCheckHotReloadNotRunning = "Hot Reload 未运行"; + OnDeviceCheckEnableExposeServer = "启用 '{0}'"; + OnDeviceCheckPlatformSelected = "已选择 {0} 平台"; + OnDeviceCheckPlatformNotSupported = "当前平台为 {0},不支持"; + OnDeviceCheckDevelopmentEnabled = "开发构建已启用"; + OnDeviceCheckEnableDevelopment = "启用“开发构建”"; + OnDeviceCheckMonoBackend = "脚本后端设置为 Mono"; + OnDeviceCheckSetMonoBackend = "将脚本后端设置为 Mono"; + OnDeviceCheckStrippingLevel = "剥离级别 = {0}"; + OnDeviceCheckStrippingSolution = "需要禁用代码剥离以确保所有方法都可用于修补。"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs.meta new file mode 100644 index 0000000..5d90713 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 19a8c5bfa6576844d9789872adce14e1 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/OnDeviceTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs new file mode 100644 index 0000000..b27dd64 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs @@ -0,0 +1,34 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Registration { + // Registration & Redeem + public static string MessageRegistrationProUsers; + public static string MessageRegistrationLicensingModel; + public static string MessageRedeemInstructions; + public static string MessageRedeemSuccess; + public static string MessageRedeemAlreadyClaimed; + public static string UnknownRedeemError; + + public static void LoadEnglish() { + // Registration & Redeem + MessageRegistrationProUsers = "Unity Pro users are required to obtain an additional license. You are eligible to redeem one if your company has ten or fewer employees. Please enter your company details below."; + MessageRegistrationLicensingModel = "The licensing model for Unity Pro users varies depending on the number of employees in your company. Please enter your company details below."; + MessageRedeemInstructions = "To enable us to verify your purchase, please enter your invoice number/order ID. Additionally, provide the email address that you intend to use for managing your credentials."; + MessageRedeemSuccess = "Success! You will receive an email containing your license password shortly. Once you receive it, please enter the received password in the designated field below to complete your registration."; + MessageRedeemAlreadyClaimed = "Your license has already been redeemed. Please enter your existing password below."; + UnknownRedeemError = "We apologize, an error happened while redeeming your license. Please reach out to customer support for assistance."; + } + + public static void LoadSimplifiedChinese() { + // Registration & Redeem + MessageRegistrationProUsers = "Unity Pro 用户需要获得额外许可证。如果您的公司有十名或更少员工,您有资格兑换一个。请在下方输入您的公司详细信息。"; + MessageRegistrationLicensingModel = "Unity Pro 用户的许可模式因公司员工人数而异。请在下方输入您的公司详细信息。"; + MessageRedeemInstructions = "为使我们能够验证您的购买,请输入您的发票号码/订单 ID。此外,请提供您打算用于管理凭据的电子邮件地址。"; + MessageRedeemSuccess = "成功!您很快将收到一封包含许可证密码的电子邮件。收到后,请在下方指定字段中输入收到的密码以完成注册。"; + MessageRedeemAlreadyClaimed = "您的许可证已被兑换。请在下方输入您现有的密码。"; + UnknownRedeemError = "我们深表歉意,兑换您的许可证时发生错误。请联系客户支持寻求帮助。"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs.meta new file mode 100644 index 0000000..7e070a1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: da7def2f6c8576e4bbb04a79c3438b46 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/RegistrationTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs new file mode 100644 index 0000000..e39299b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs @@ -0,0 +1,283 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Settings { + // Settings Tab + public static string SettingsTitle; + public static string SettingsConfiguration; + public static string SettingsAdvanced; + public static string SettingsOnDevice; + public static string SettingsManualConnect; + public static string SettingsBuildSettingsChecklist; + public static string SettingsOptions; + public static string SettingsVisualFeedback; + public static string SettingsMisc; + + // Settings Descriptions + public static string SettingsManageAutoRefreshOn; + public static string SettingsManageAutoRefreshOff; + public static string SettingsAssetRefreshOn; + public static string SettingsAssetRefreshOff; + public static string SettingsDebuggerCompatibilityOn; + public static string SettingsDebuggerCompatibilityOff; + public static string SettingsRefreshShadersOn; + public static string SettingsRefreshShadersOff; + public static string SettingsHideConsoleOn; + public static string SettingsHideConsoleOff; + public static string SettingsDeactivatedOn; + public static string SettingsDeactivatedOff; + public static string SettingsDisableErrorReportingOn; + public static string SettingsDisableErrorReportingOff; + public static string SettingsPauseEditModeOn; + public static string SettingsPauseEditModeOff; + public static string SettingsAutostartOn; + public static string SettingsAutostartOff; + public static string SettingsAutoClearTimelineOn; + public static string SettingsAutoClearTimelineOff; + public static string SettingsPatchingIndicationUnsupported; + public static string SettingsPatchingIndicationOff; + public static string SettingsPatchingIndicationOn; + public static string SettingsCompilingIndicationUnsupported; + public static string SettingsCompilingIndicationOff; + public static string SettingsCompilingIndicationOn; + public static string SettingsAutoRecompileUnsupported; + public static string SettingsAutoRecompileOn; + public static string SettingsAutoRecompileOff; + public static string SettingsAutoRecompileInspectorOn; + public static string SettingsAutoRecompileInspectorOff; + public static string SettingsAutoRecompilePartialOn; + public static string SettingsAutoRecompilePartialOff; + public static string SettingsDisplayMonobehaviourOn; + public static string SettingsDisplayMonobehaviourOff; + public static string SettingsRecompileImmediatelyOn; + public static string SettingsRecompileImmediatelyOff; + public static string SettingsRecompilePlayModeOn; + public static string SettingsRecompilePlayModeOff; + public static string SettingsRecompileEditModeOn; + public static string SettingsRecompileEditModeOff; + public static string SettingsRecompileExitPlayModeOn; + public static string SettingsRecompileExitPlayModeOff; + public static string SettingsIndicationsUnsupported; + + // Settings Toggle Names + public static string ToggleManageAutoRefresh; + public static string ToggleAssetRefresh; + public static string ToggleDebuggerCompatibility; + public static string ToggleRefreshShaders; + public static string ToggleHideConsole; + public static string ToggleDeactivate; + public static string ToggleDisableErrorReporting; + public static string TogglePauseEditMode; + public static string ToggleAutostart; + public static string ToggleAutoClearTimeline; + public static string TogglePatchingIndication; + public static string ToggleCompilingIndication; + public static string ToggleAutoRecompile; + public static string ToggleAutoRecompileInspector; + public static string ToggleAutoRecompilePartial; + public static string ToggleDisplayMonobehaviour; + public static string ToggleRecompileImmediately; + public static string ToggleRecompilePlayMode; + public static string ToggleRecompileEditMode; + public static string ToggleRecompileExitPlayMode; + + // Settings Options + public static string OptionExposeServerShort; + public static string OptionExposeServerFull; + public static string OptionExposeServerDescriptionEnabled; + public static string OptionExposeServerDescriptionDisabled; + public static string OptionAllowHttpRequests; + public static string OptionAllowHttpRequestsDescription; + public static string OptionIncludeInBuild; + public static string OptionIncludeInBuildDescriptionEnabled; + public static string OptionIncludeInBuildDescriptionDisabled; + public static string OptionIncludeInBuildDescriptionSuffix; + + public static void LoadEnglish() { + // Settings Tab + SettingsTitle = "Settings"; + SettingsConfiguration = "Settings"; + SettingsAdvanced = "Advanced"; + SettingsOnDevice = "On-Device"; + SettingsManualConnect = "Manual connect"; + SettingsBuildSettingsChecklist = "Build Settings Checklist"; + SettingsOptions = "Options"; + SettingsVisualFeedback = "Visual Feedback"; + SettingsMisc = "Misc"; + + // Settings Descriptions + SettingsManageAutoRefreshOn = "To avoid unnecessary recompiling, Hot Reload will automatically change Unity's Auto Refresh and Script Compilation settings. Previous settings will be restored when Hot Reload is stopped"; + SettingsManageAutoRefreshOff = "Enabled this setting to auto-manage Unity's Auto Refresh and Script Compilation settings. This reduces unncessary recompiling"; + SettingsAssetRefreshOn = "Hot Reload will refresh changed assets such as sprites, prefabs, etc"; + SettingsAssetRefreshOff = "Enable to allow Hot Reload to refresh changed assets in the project. All asset types are supported including sprites, prefabs, shaders etc"; + SettingsDebuggerCompatibilityOn = "Hot Reload automatically disables itself while a debugger is attached, as it can otherwise interfere with certain debugger features. Please read the documentation if you consider disabling this setting."; + SettingsDebuggerCompatibilityOff = "When a debugger is attached, Hot Reload will be active, but certain debugger features might not work as expected. Please read our documentation to learn about the limitations."; + SettingsRefreshShadersOn = "Hot Reload will auto refresh shaders. Note that enabling this setting might impact performance."; + SettingsRefreshShadersOff = "Enable to auto-refresh shaders. Note that enabling this setting might impact performance"; + SettingsHideConsoleOn = "Hot Reload will start without creating a console window. Logs can be accessed through \"Help\" tab."; + SettingsHideConsoleOff = "Enable to start Hot Reload without creating a console window."; + SettingsDeactivatedOn = "Hot Reload is deactivated."; + SettingsDeactivatedOff = "Enable to deactivate Hot Reload."; + SettingsDisableErrorReportingOn = "Detailed error reporting is disabled."; + SettingsDisableErrorReportingOff = "Toggle on to disable detailed error reporting."; + SettingsPauseEditModeOn = "Hot Reload is paused in Edit mode. It is recommended to perform a full Unity recompilation manually before entering Play Mode to prevent Hot Reload becoming unusable."; + SettingsPauseEditModeOff = "Toggle on to pause Hot Reload while in Edit mode. With this setting enabled, it is recommended to perform a full Unity recompilation manually before entering Play Mode to prevent Hot Reload becoming unusable."; + SettingsAutostartOn = "Hot Reload will be launched when Unity project opens."; + SettingsAutostartOff = "Enable to launch Hot Reload when Unity project opens."; + SettingsAutoClearTimelineOn = "Timeline entries will be cleared after every full Unity recompilation."; + SettingsAutoClearTimelineOff = "Enable to clear Timeline entries after every full Unity recompilation."; + SettingsPatchingIndicationUnsupported = "Patching Notification is not supported in the Unity version you use."; + SettingsPatchingIndicationOff = "Enable to show GameView and SceneView indications when Patching."; + SettingsPatchingIndicationOn = "Indications will be shown in GameView and SceneView when Patching."; + SettingsCompilingIndicationUnsupported = "Compiling Unsupported Changes Notification is not supported in the Unity version you use."; + SettingsCompilingIndicationOff = "Enable to show GameView and SceneView indications when compiling unsupported changes."; + SettingsCompilingIndicationOn = "Indications will be shown in GameView and SceneView when compiling unsupported changes."; + SettingsAutoRecompileUnsupported = "Auto recompiling unsupported changes is not supported in the Unity version you use."; + SettingsAutoRecompileOn = "Hot Reload will recompile automatically after code changes that Hot Reload doesn't support."; + SettingsAutoRecompileOff = "When enabled, recompile happens automatically after code changes that Hot Reload doesn't support."; + SettingsAutoRecompileInspectorOn = "Hot Reload will trigger recompilation for inspector field changes that are not supported in Edit mode."; + SettingsAutoRecompileInspectorOff = "Enable to trigger recompilation for inspector field changes that are not supported in Edit mode."; + SettingsAutoRecompilePartialOn = "Hot Reload will recompile partially supported changes."; + SettingsAutoRecompilePartialOff = "Enable to recompile partially supported changes."; + SettingsDisplayMonobehaviourOn = "Hot Reload will display new monobehaviour methods as partially supported."; + SettingsDisplayMonobehaviourOff = "Enable to display new monobehaviour methods as partially supported."; + SettingsRecompileImmediatelyOn = "Unsupported changes will be recompiled immediately."; + SettingsRecompileImmediatelyOff = "Unsupported changes will be recompiled when editor is focused. Enable to recompile immediately."; + SettingsRecompilePlayModeOn = "Hot Reload will exit Play Mode to recompile unsupported changes."; + SettingsRecompilePlayModeOff = "Enable to auto exit Play Mode to recompile unsupported changes."; + SettingsRecompileEditModeOn = "Hot Reload recompile unsupported changes when in Edit Mode."; + SettingsRecompileEditModeOff = "Enable to auto recompile unsupported changes in Edit Mode."; + SettingsRecompileExitPlayModeOn = "Hot Reload will recompile unsupported changes when exiting Play Mode."; + SettingsRecompileExitPlayModeOff = "Enable to recompile unsupported changes when exiting Play Mode."; + SettingsIndicationsUnsupported = "Indications are not supported in the Unity version you use."; + + // Settings Toggle Names + ToggleManageAutoRefresh = "Manage Unity auto-refresh (recommended)"; + ToggleAssetRefresh = "Asset refresh (recommended)"; + ToggleDebuggerCompatibility = "Auto-disable Hot Reload while a debugger is attached (recommended)"; + ToggleRefreshShaders = "Refresh shaders"; + ToggleHideConsole = "Hide console window on start"; + ToggleDeactivate = "Deactivate Hot Reload"; + ToggleDisableErrorReporting = "Disable Detailed Error Reporting"; + TogglePauseEditMode = "Pause Hot Reload in Edit Mode"; + ToggleAutostart = "Autostart on Unity open"; + ToggleAutoClearTimeline = "Auto clear Timeline after recompile"; + TogglePatchingIndication = "Patching Indication"; + ToggleCompilingIndication = "Compiling Unsupported Changes Indication"; + ToggleAutoRecompile = "Auto recompile unsupported changes (recommended)"; + ToggleAutoRecompileInspector = "Auto recompile inspector field edits"; + ToggleAutoRecompilePartial = "Include partially supported changes"; + ToggleDisplayMonobehaviour = "Display new Monobehaviour methods as partially supported"; + ToggleRecompileImmediately = "Recompile immediately"; + ToggleRecompilePlayMode = "Recompile in Play Mode"; + ToggleRecompileEditMode = "Recompile in Edit Mode"; + ToggleRecompileExitPlayMode = "Recompile on exit Play Mode"; + + // Settings Options + OptionExposeServerShort = "Allow Devices to Connect"; + OptionExposeServerFull = "Allow Devices to Connect (WiFi)"; + OptionExposeServerDescriptionEnabled = "The HotReload server is reachable from devices on the same Wifi network"; + OptionExposeServerDescriptionDisabled = "The HotReload server is available to your computer only. Other devices cannot connect to it."; + OptionAllowHttpRequests = "Allow app to make HTTP requests"; + OptionAllowHttpRequestsDescription = "For Hot Reload to work on-device, please allow HTTP requests"; + OptionIncludeInBuild = "Include Hot Reload in player builds"; + OptionIncludeInBuildDescriptionEnabled = "The Hot Reload runtime is included in development builds that use the Mono scripting backend."; + OptionIncludeInBuildDescriptionDisabled = "The Hot Reload runtime will not be included in any build. Use this option to disable HotReload without removing it from your project."; + OptionIncludeInBuildDescriptionSuffix = " This option does not affect Hot Reload usage in Playmode"; + } + + public static void LoadSimplifiedChinese() { + // Settings Tab + SettingsTitle = "设置"; + SettingsConfiguration = "配置"; + SettingsAdvanced = "高级"; + SettingsOnDevice = "在设备上"; + SettingsManualConnect = "手动连接"; + SettingsBuildSettingsChecklist = "构建设置清单"; + SettingsOptions = "选项"; + SettingsVisualFeedback = "视觉反馈"; + SettingsMisc = "杂项"; + + // Settings Descriptions + SettingsManageAutoRefreshOn = "为避免不必要的重新编译,Hot Reload 将自动更改 Unity 的自动刷新和脚本编译设置。停止 Hot Reload 后将恢复以前的设置"; + SettingsManageAutoRefreshOff = "启用此设置以自动管理 Unity 的自动刷新和脚本编译设置。这可以减少不必要的重新编译"; + SettingsAssetRefreshOn = "Hot Reload 将刷新已更改的资产,如精灵、预制件等"; + SettingsAssetRefreshOff = "启用以允许 Hot Reload 刷新项目中已更改的资产。支持所有资产类型,包括精灵、预制件、着色器等"; + SettingsDebuggerCompatibilityOn = "附加调试器时,Hot Reload 会自动禁用自身,因为它可能会干扰某些调试器功能。如果您考虑禁用此设置,请阅读文档。"; + SettingsDebuggerCompatibilityOff = "附加调试器时,Hot Reload 将处于活动状态,但某些调试器功能可能无法按预期工作。请阅读我们的文档以了解限制。"; + SettingsRefreshShadersOn = "Hot Reload 将自动刷新着色器。请注意,启用此设置可能会影响性能。"; + SettingsRefreshShadersOff = "启用以自动刷新着色器。请注意,启用此设置可能会影响性能"; + SettingsHideConsoleOn = "Hot Reload 启动时不会创建控制台窗口。可以通过“帮助”选项卡访问日志。"; + SettingsHideConsoleOff = "启用以在不创建控制台窗口的情况下启动 Hot Reload。"; + SettingsDeactivatedOn = "Hot Reload 已停用。"; + SettingsDeactivatedOff = "启用以停用 Hot Reload。"; + SettingsDisableErrorReportingOn = "详细错误报告已禁用。"; + SettingsDisableErrorReportingOff = "切换以禁用详细错误报告。"; + SettingsPauseEditModeOn = "在编辑模式下,Hot Reload 已暂停。建议在进入播放模式之前手动执行完整的 Unity 重新编译,以防止 Hot Reload 无法使用。"; + SettingsPauseEditModeOff = "切换以在编辑模式下暂停 Hot Reload。启用此设置后,建议在进入播放模式之前手动执行完整的 Unity 重新编译,以防止 Hot Reload 无法使用。"; + SettingsAutostartOn = "当 Unity 项目打开时,将启动 Hot Reload。"; + SettingsAutostartOff = "启用以在 Unity 项目打开时启动 Hot Reload。"; + SettingsAutoClearTimelineOn = "每次完整的 Unity 重新编译后,时间轴条目将被清除。"; + SettingsAutoClearTimelineOff = "启用后,每次完整的 Unity 重新编译后清除时间轴条目。"; + SettingsPatchingIndicationUnsupported = "您使用的 Unity 版本不支持修补通知。"; + SettingsPatchingIndicationOff = "启用以在修补时显示 GameView 和 SceneView 指示。"; + SettingsPatchingIndicationOn = "修补时将在 GameView 和 SceneView 中显示指示。"; + SettingsCompilingIndicationUnsupported = "您使用的 Unity 版本不支持编译不支持的更改通知。"; + SettingsCompilingIndicationOff = "启用以在编译不支持的更改时显示 GameView 和 SceneView 指示。"; + SettingsCompilingIndicationOn = "编译不支持的更改时将在 GameView 和 SceneView 中显示指示。"; + SettingsAutoRecompileUnsupported = "您使用的 Unity 版本不支持自动重新编译不支持的更改。"; + SettingsAutoRecompileOn = "在 Hot Reload 不支持的代码更改后,Hot Reload 将自动重新编译。"; + SettingsAutoRecompileOff = "启用后,在 Hot Reload 不支持的代码更改后会自动进行重新编译。"; + SettingsAutoRecompileInspectorOn = "对于在编辑模式下不支持的检查器字段更改,Hot Reload 将触发重新编译。"; + SettingsAutoRecompileInspectorOff = "启用以在编辑模式下为不支持的检查器字段更改触发重新编译。"; + SettingsAutoRecompilePartialOn = "Hot Reload 将重新编译部分不支持的更改。"; + SettingsAutoRecompilePartialOff = "启用以重新编译部分不支持的更改。"; + SettingsDisplayMonobehaviourOn = "Hot Reload 将把新的 monobehaviour 方法显示为部分不支持。"; + SettingsDisplayMonobehaviourOff = "启用以将新的 monobehaviour 方法显示为部分不支持。"; + SettingsRecompileImmediatelyOn = "不支持的更改将立即重新编译。"; + SettingsRecompileImmediatelyOff = "当编辑器获得焦点时,将重新编译不支持的更改。启用以立即重新编译。"; + SettingsRecompilePlayModeOn = "Hot Reload 将退出播放模式以重新编译不支持的更改。"; + SettingsRecompilePlayModeOff = "启用以自动退出播放模式以重新编译不支持的更改。"; + SettingsRecompileEditModeOn = "Hot Reload 在编辑模式下重新编译不支持的更改。"; + SettingsRecompileEditModeOff = "启用以在编辑模式下自动重新编译不支持的更改。"; + SettingsRecompileExitPlayModeOn = "退出播放模式时,Hot Reload 将重新编译不支持的更改。"; + SettingsRecompileExitPlayModeOff = "启用以在退出播放模式时重新编译不支持的更改。"; + SettingsIndicationsUnsupported = "您使用的 Unity 版本不支持指示。"; + + // Settings Toggle Names + ToggleManageAutoRefresh = "管理 Unity 自动刷新(推荐)"; + ToggleAssetRefresh = "资产刷新(推荐)"; + ToggleDebuggerCompatibility = "附加调试器时自动禁用 Hot Reload(推荐)"; + ToggleRefreshShaders = "刷新着色器"; + ToggleHideConsole = "启动时隐藏控制台窗口"; + ToggleDeactivate = "停用 Hot Reload"; + ToggleDisableErrorReporting = "禁用详细错误报告"; + TogglePauseEditMode = "在编辑模式下暂停 Hot Reload"; + ToggleAutostart = "在 Unity 打开时自动启动"; + ToggleAutoClearTimeline = "重新编译后自动清除时间轴"; + TogglePatchingIndication = "修补指示"; + ToggleCompilingIndication = "编译不支持的更改指示"; + ToggleAutoRecompile = "自动重新编译不支持的更改(推荐)"; + ToggleAutoRecompileInspector = "自动重新编译检查器字段编辑"; + ToggleAutoRecompilePartial = "包括部分不支持的更改"; + ToggleDisplayMonobehaviour = "将新的 Monobehaviour 方法显示为部分支持"; + ToggleRecompileImmediately = "立即重新编译"; + ToggleRecompilePlayMode = "在播放模式下重新编译"; + ToggleRecompileEditMode = "在编辑模式下重新编译"; + ToggleRecompileExitPlayMode = "退出播放模式时重新编译"; + + // Settings Options + OptionExposeServerShort = "允许设备连接"; + OptionExposeServerFull = "允许设备连接 (WiFi)"; + OptionExposeServerDescriptionEnabled = "HotReload 服务器可从同一 Wifi 网络上的设备访问"; + OptionExposeServerDescriptionDisabled = "HotReload 服务器仅对您的计算机可用。其他设备无法连接到它。"; + OptionAllowHttpRequests = "允许应用发出 HTTP 请求"; + OptionAllowHttpRequestsDescription = "为了让 Hot Reload 在设备上工作,请允许 HTTP 请求"; + OptionIncludeInBuild = "在播放器构建中包含 Hot Reload"; + OptionIncludeInBuildDescriptionEnabled = "Hot Reload 运行时包含在使用 Mono 脚本后端的开发构建中。"; + OptionIncludeInBuildDescriptionDisabled = "Hot Reload 运行时将不包含在任何构建中。使用此选项可在不从项目中删除 HotReload 的情况下禁用它。"; + OptionIncludeInBuildDescriptionSuffix = " 此选项不影响在播放模式下使用 Hot Reload"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs.meta new file mode 100644 index 0000000..f0d55e1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: df46693e37527a74b8c24e95da9a4c47 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/SettingsTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs new file mode 100644 index 0000000..693e6db --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs @@ -0,0 +1,269 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Suggestions { + // Button texts + public static string ButtonDocs; + public static string ButtonMoreInfo; + public static string ButtonVote; + public static string ButtonUseBuildTimeOnlyAtlas; + public static string ButtonOpenSettings; + public static string ButtonIgnoreSuggestion; + public static string ButtonLearnMore; + public static string ButtonStopHotReload; + public static string ButtonDontShowAgain; + public static string ButtonOK; + public static string ButtonOKPadded; + public static string ButtonDisable; + public static string ButtonTurnOff; + public static string ButtonAutoRecompile; + public static string ButtonSwitchToDebugMode; + public static string ButtonKeepEnabledDuringDebugging; + public static string ButtonRecompile; + + // Unity Best Development Tool Award 2023 + public static string Award2023Title; + public static string Award2023Message; + + // Unsupported Changes + public static string UnsupportedChangesTitle; + public static string UnsupportedChangesMessage; + + // Unsupported Packages + public static string UnsupportedPackagesTitle; + public static string UnsupportedPackagesMessage; + + // Auto Recompiled When Playmode State Changes + public static string AutoRecompiledPlaymodeTitle; + public static string AutoRecompiledPlaymodeMessage; + + // Auto Recompiled When Playmode State Changes 2022 + public static string AutoRecompiled2022Title; + public static string AutoRecompiled2022Message; + + // Multidimensional Arrays + public static string MultidimensionalArraysTitle; + public static string MultidimensionalArraysMessage; + + // Editors Without HR Running + public static string EditorsWithoutHRTitle; + public static string EditorsWithoutHRMessage; + + // Field Initializer With Side Effects + public static string FieldInitializerSideEffectsTitle; + public static string FieldInitializerSideEffectsMessage; + + // Detailed Error Reporting Is Enabled + public static string DetailedErrorReportingTitle; + public static string DetailedErrorReportingMessage; + + // Field Initializer Existing Instances Edited + public static string FieldInitializerEditedTitle; + public static string FieldInitializerEditedMessage; + + // Field Initializer Existing Instances Unedited + public static string FieldInitializerUneditedTitle; + public static string FieldInitializerUneditedMessage; + + // Add Monobehaviour Method + public static string AddMonobehaviourMethodTitle; + public static string AddMonobehaviourMethodMessage; + + // Switch To Debug Mode For Inlined Methods + public static string SwitchToDebugModeTitle; + public static string SwitchToDebugModeMessage; + public static string SwitchToDebugModeConfirmation; + + // Hot Reload While Debugger Is Attached + public static string DebuggerAttachedTitle; + public static string DebuggerAttachedMessagePaused; + public static string DebuggerAttachedMessageAutoRecompile; + + + // Hot Reloaded Methods When Debugger Is Attached + public static string DebuggerMethodsTitle; + public static string DebuggerMethodsMessage; + public static string DebuggerMethodsConfirmation; + + // Hot Reloaded Requires UTF8 Encoding + public static string UTF8EncodingRequiredTitle; + public static string UTF8EncodingRequiredMessage; + + public static void LoadEnglish() { + // Button texts + ButtonDocs = "Docs"; + ButtonMoreInfo = "More Info"; + ButtonVote = " Vote "; + ButtonUseBuildTimeOnlyAtlas = " Use \"Build Time Only Atlas\" "; + ButtonOpenSettings = " Open Settings "; + ButtonIgnoreSuggestion = " Ignore suggestion "; + ButtonLearnMore = " Learn more "; + ButtonStopHotReload = " Stop Hot Reload "; + ButtonDontShowAgain = " Don't show again "; + ButtonOK = " OK "; + ButtonOKPadded = " OK "; + ButtonDisable = " Disable "; + ButtonTurnOff = " Turn off "; + ButtonAutoRecompile = " Auto Recompile "; + ButtonSwitchToDebugMode = " Switch to Debug mode "; + ButtonKeepEnabledDuringDebugging = " Keep enabled during debugging "; + ButtonRecompile = " Recompile "; + + // Unity Best Development Tool Award 2023 + Award2023Title = "Vote for the \"Best Development Tool\" Award!"; + Award2023Message = "Hot Reload was nominated for the \"Best Development Tool\" Award. Please consider voting. Thank you!"; + + // Unsupported Changes + UnsupportedChangesTitle = "Which changes does Hot Reload support?"; + UnsupportedChangesMessage = "Hot Reload supports most code changes, but there are some limitations. Generally, changes to methods and fields are supported. Things like adding new types is not (yet) supported. See the documentation for the list of current features and our current roadmap"; + + // Unsupported Packages + UnsupportedPackagesTitle = "Unsupported package detected"; + UnsupportedPackagesMessage = "The following packages are only partially supported: ECS, Mirror, Fishnet, and Photon. Hot Reload will work in the project, but changes specific to those packages might not hot-reload"; + + // Auto Recompiled When Playmode State Changes + AutoRecompiledPlaymodeTitle = "Unity recompiles on enter/exit play mode?"; + AutoRecompiledPlaymodeMessage = "If you have an issue with the Unity Editor recompiling when the Play Mode state changes, more info is available in the docs. Feel free to reach out if you require assistance. We'll be glad to help."; + + // Auto Recompiled When Playmode State Changes 2022 + AutoRecompiled2022Title = "Unsupported setting detected"; + AutoRecompiled2022Message = "The 'Sprite Packer Mode' setting can cause unintended recompilations if set to 'Sprite Atlas V1 - Always Enabled'"; + + // Multidimensional Arrays + MultidimensionalArraysTitle = "Use jagged instead of multidimensional arrays"; + MultidimensionalArraysMessage = "Hot Reload doesn't support methods with multidimensional arrays ([,]). You can work around this by using jagged arrays ([][])"; + + // Editors Without HR Running + EditorsWithoutHRTitle = "Some Unity instances don't have Hot Reload running."; + EditorsWithoutHRMessage = "Make sure that either: \n1) Hot Reload is installed and running on all Editor instances, or \n2) Hot Reload is stopped in all Editor instances where it is installed."; + + // Field Initializer With Side Effects + FieldInitializerSideEffectsTitle = "Field initializer with side-effects detected"; + FieldInitializerSideEffectsMessage = "A field initializer update might have side effects, e.g. calling a method or creating an object.\n\nWhile Hot Reload does support this, it can sometimes be confusing when the initializer logic runs at 'unexpected times'."; + + // Detailed Error Reporting Is Enabled + DetailedErrorReportingTitle = "Detailed error reporting is enabled"; + DetailedErrorReportingMessage = "When an error happens in Hot Reload, the exception stacktrace is sent as telemetry to help diagnose and fix the issue.\nThe exception stack trace is only included if it originated from the Hot Reload package or binary. Stacktraces from your own code are not sent.\nYou can disable detailed error reporting to prevent telemetry from including any information about your project."; + + // Field Initializer Existing Instances Edited + FieldInitializerEditedTitle = "Field initializer edit updated the value of existing class instances"; + FieldInitializerEditedMessage = "By default, Hot Reload updates field values of existing object instances when new field initializer has constant value.\n\nIf you want to change this behavior, disable the \"Apply field initializer edits to existing class instances\" option in Settings or click the button below."; + + // Field Initializer Existing Instances Unedited + FieldInitializerUneditedTitle = "Field initializer edits don't apply to existing objects"; + FieldInitializerUneditedMessage = "By default, Hot Reload applies field initializer edits of existing fields only to new objects (newly instantiated classes), just like normal C#.\n\nFor rapid prototyping, you can use static fields which will update across all instances."; + + // Add Monobehaviour Method + AddMonobehaviourMethodTitle = "New MonoBehaviour methods are not shown in the inspector"; + AddMonobehaviourMethodMessage = "New methods in MonoBehaviours are not shown in the inspector until the script is recompiled. This is a limitation of Hot Reload handling of Unity's serialization system.\n\nYou can use the button below to auto recompile partially supported changes such as this one."; + + // Switch To Debug Mode For Inlined Methods + SwitchToDebugModeTitle = "Switch code optimization to Debug Mode"; + SwitchToDebugModeMessage = "In Release Mode some methods are inlined, which prevents Hot Reload from applying changes. A clear warning is always shown when this happens, but you can use Debug Mode to avoid the issue altogether"; + SwitchToDebugModeConfirmation = "Switching code optimization will stop Play Mode.\n\nDo you wish to proceed?"; + + // Hot Reload While Debugger Is Attached + DebuggerAttachedTitle = "Hot Reload is disabled while a debugger is attached"; + DebuggerAttachedMessageAutoRecompile = "Hot Reload automatically disables itself while a debugger is attached, as it can otherwise interfere with certain debugger features.\nWhile disabled, every code change will trigger a full Unity recompilation.\n\nYou can choose to keep Hot Reload enabled while a debugger is attached, though some features like debugger variable inspection might not always work as expected."; + DebuggerAttachedMessagePaused = "Hot Reload automatically disables itself while a debugger is attached, as it can otherwise interfere with certain debugger features.\n\nYou can choose to keep Hot Reload enabled while a debugger is attached, though some features like debugger variable inspection might not always work as expected."; + + // Hot Reloaded Methods When Debugger Is Attached + DebuggerMethodsTitle = "Hot Reload may interfere with your debugger session"; + DebuggerMethodsMessage = "Some debugger features, like variable inspection, might not work as expected for methods patched during the Hot Reload session. A full Unity recompile is required to get the full debugger experience."; + DebuggerMethodsConfirmation = "Using the Recompile button will stop Play Mode.\n\nDo you wish to proceed?"; + + // Hot Reloaded Requires UTF8 Encoding + UTF8EncodingRequiredTitle = "Change file encoding to UTF-8"; + UTF8EncodingRequiredMessage = "Unknown source file encoding detected. Change the encoding of the code editor to UTF-8 to resolve this problem."; + } + + public static void LoadSimplifiedChinese() { + // Button texts + ButtonDocs = "文档"; + ButtonMoreInfo = "更多信息"; + ButtonVote = " 投票 "; + ButtonUseBuildTimeOnlyAtlas = " 使用“仅构建时图集” "; + ButtonOpenSettings = " 打开设置 "; + ButtonIgnoreSuggestion = " 忽略建议 "; + ButtonLearnMore = " 了解更多 "; + ButtonStopHotReload = " 停止 Hot Reload "; + ButtonDontShowAgain = " 不再显示 "; + ButtonOK = " 确定 "; + ButtonOKPadded = " 确定 "; + ButtonDisable = " 禁用 "; + ButtonTurnOff = " 关闭 "; + ButtonAutoRecompile = " 自动重新编译 "; + ButtonSwitchToDebugMode = " 切换到调试模式 "; + ButtonKeepEnabledDuringDebugging = " 在调试期间保持启用 "; + ButtonRecompile = " 重新编译 "; + + // Unity Best Development Tool Award 2023 + Award2023Title = "为“最佳开发工具”奖投票!"; + Award2023Message = "Hot Reload 被提名为“最佳开发工具”奖。请考虑投票。谢谢!"; + + // Unsupported Changes + UnsupportedChangesTitle = "Hot Reload 支持哪些更改?"; + UnsupportedChangesMessage = "Hot Reload 支持大多数代码更改,但存在一些限制。通常支持对方法和字段的更改。尚不支持添加新类型等操作。有关当前功能列表和我们当前路线图,请参阅文档"; + + // Unsupported Packages + UnsupportedPackagesTitle = "检测到不支持的包"; + UnsupportedPackagesMessage = "以下包仅部分支持:ECS、Mirror、Fishnet 和 Photon。Hot Reload 可以在项目中使用,但针对这些包的特定更改可能无法热重载"; + + // Auto Recompiled When Playmode State Changes + AutoRecompiledPlaymodeTitle = "Unity 在进入/退出播放模式时重新编译?"; + AutoRecompiledPlaymodeMessage = "如果您在播放模式状态更改时遇到 Unity 编辑器重新编译的问题,可以在文档中找到更多信息。如果您需要帮助,请随时与我们联系。我们很乐意提供帮助。"; + + // Auto Recompiled When Playmode State Changes 2022 + AutoRecompiled2022Title = "检测到不支持的设置"; + AutoRecompiled2022Message = "如果“精灵打包器模式”设置为“精灵图集 V1 - 始终启用”,则可能导致意外的重新编译"; + + // Multidimensional Arrays + MultidimensionalArraysTitle = "使用交错数组代替多维数组"; + MultidimensionalArraysMessage = "Hot Reload 不支持带有二维数组 ([,]) 的方法。您可以通过使用交错数组 ([][]) 来解决此问题"; + + // Editors Without HR Running + EditorsWithoutHRTitle = "一些 Unity 实例没有运行 Hot Reload。"; + EditorsWithoutHRMessage = "请确保:\n1) 在所有编辑器实例上安装并运行 Hot Reload,或\n2) 在所有安装了 Hot Reload 的编辑器实例中停止它。"; + + // Field Initializer With Side Effects + FieldInitializerSideEffectsTitle = "检测到带有副作用的字段初始化器"; + FieldInitializerSideEffectsMessage = "字段初始化器更新可能有副作用,例如调用方法或创建对象。\n\n虽然 Hot Reload 支持此功能,但当初始化器逻辑在“意外时间”运行时,有时可能会令人困惑。"; + + // Detailed Error Reporting Is Enabled + DetailedErrorReportingTitle = "已启用详细错误报告"; + DetailedErrorReportingMessage = "当 Hot Reload 中发生错误时,异常堆栈跟踪将作为遥测数据发送,以帮助诊断和修复问题。\n仅当异常堆栈跟踪源自 Hot Reload 包或二进制文件时才会包含。不会发送您自己代码的堆栈跟踪。\n您可以禁用详细错误报告,以防止遥测数据包含有关您项目的任何信息。"; + + // Field Initializer Existing Instances Edited + FieldInitializerEditedTitle = "字段初始化器编辑更新了现有类实例的值"; + FieldInitializerEditedMessage = "默认情况下,当新的字段初始化器具有常量值时,Hot Reload 会更新现有对象实例的字段值。\n\n如果您想更改此行为,请在“设置”中禁用“将字段初始化器编辑应用于现有类实例”选项,或单击下面的按钮。"; + + // Field Initializer Existing Instances Unedited + FieldInitializerUneditedTitle = "字段初始化器编辑不适用于现有对象"; + FieldInitializerUneditedMessage = "默认情况下,Hot Reload 仅将现有字段的字段初始化器编辑应用于新对象(新实例化的类),就像普通的 C# 一样。\n\n为了快速原型制作,您可以使用静态字段,它将在所有实例中更新。"; + + // Add Monobehaviour Method + AddMonobehaviourMethodTitle = "新的 MonoBehaviour 方法不会显示在检查器中"; + AddMonobehaviourMethodMessage = "在重新编译脚本之前,MonoBehaviours 中的新方法不会显示在检查器中。这是 Hot Reload 处理 Unity 序列化系统的限制。\n\n您可以使用下面的按钮自动重新编译部分支持的更改,例如此更改。"; + + // Switch To Debug Mode For Inlined Methods + SwitchToDebugModeTitle = "将代码优化切换到调试模式"; + SwitchToDebugModeMessage = "在发布模式下,某些方法是内联的,这会阻止 Hot Reload 应用更改。发生这种情况时总是会显示明确的警告,但您可以使用调试模式完全避免此问题"; + SwitchToDebugModeConfirmation = "切换代码优化将停止播放模式。\n\n您希望继续吗?"; + + // Hot Reload While Debugger Is Attached + DebuggerAttachedTitle = "附加调试器时禁用 Hot Reload"; + DebuggerAttachedMessageAutoRecompile = "当附加调试器时,热重载将自动禁用,以避免干扰某些调试器功能。\n禁用状态下,每次修改代码都会触发Unity完整重编译。\n\n您也可以选择在附加调试器时继续启用热重载,但请注意某些功能(例如调试器的变量检查)可能无法正常工作。"; + DebuggerAttachedMessagePaused = "当附加调试器时,热重载将自动禁用,以避免干扰某些调试器功能。\n\n您也可以选择在附加调试器时继续启用热重载,但请注意某些功能(例如调试器的变量检查)可能无法正常工作。"; + + // Hot Reloaded Methods When Debugger Is Attached + DebuggerMethodsTitle = "Hot Reload 可能会干扰您的调试会话"; + DebuggerMethodsMessage = "某些调试器功能,例如变量检查,对于在 Hot Reload 会话期间修补的方法可能无法按预期工作。需要完整的 Unity 重新编译才能获得完整的调试器体验。"; + DebuggerMethodsConfirmation = "使用“重新编译”按钮将停止播放模式。\n\n您希望继续吗?"; + + // Hot Reloaded Requires UTF8 Encoding + UTF8EncodingRequiredTitle = "将文件编码更改为 UTF-8"; + UTF8EncodingRequiredMessage = "检测到未知的源文件编码。请将代码编辑器的编码更改为 UTF-8 以解决此问题。"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs.meta new file mode 100644 index 0000000..42c2e65 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3a2d7e9f8b1c4a5e9d6f8a7b9c1d2e3f +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/SuggestionsTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs new file mode 100644 index 0000000..851069c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs @@ -0,0 +1,154 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Timeline { + // Timeline/Events + public static string TimelineTitle; + public static string SuggestionsTitle; + public static string MessageCompleteRegistration; + public static string MessageUseStartButton; + public static string MessageEnableFilters; + public static string MessageMakeCodeChanges; + public static string MessageOnly40EntriesShown; + public static string EventsTooltip; + public static string LabelSuggestionsFormat; + public static string LabelTimeline; + public static string ButtonIgnoreEventType; + public static string MessageStartHotReload; + + // Partially Supported Change Descriptions + public static string PartiallySupportedLambdaClosure; + public static string PartiallySupportedEditAsyncMethod; + public static string PartiallySupportedAddMonobehaviourMethod; + public static string PartiallySupportedEditMonobehaviourField; + public static string PartiallySupportedEditCoroutine; + public static string PartiallySupportedEditGenericFieldInitializer; + public static string PartiallySupportedAddEnumMember; + public static string PartiallySupportedEditFieldInitializer; + public static string PartiallySupportedAddMethodWithAttributes; + public static string PartiallySupportedAddFieldWithAttributes; + public static string PartiallySupportedGenericMethodInGenericClass; + public static string PartiallySupportedNewCustomSerializableField; + public static string PartiallySupportedMultipleFieldsEditedInTheSameType; + + // Event Entry Titles and Descriptions + public static string EventTitleFailedApplyingPatch; + public static string EventDescriptionInlinedMethods; + public static string EventDescriptionNoIssuesFound; + public static string EventDescriptionSeeUnsupportedChangesBelow; + public static string EventDescriptionSeeDetailedEntriesBelow; + public static string EventDescriptionSeePartiallyAppliedChangesBelow; + public static string EventDescriptionUndetectedChange; + public static string EventTitleChangePartiallyApplied; + public static string EventDescriptionFailedApplyingPatchTapForMore; + + // Manual download + public static string ManualDownloadButtonContactSupport; + public static string ManualDownloadWarning; + public static string ManualDownloadInfo; + public static string ManualDownloadButtonCopyToClipboard; + public static string ManualDownloadButtonOpenDownloadUrl; + public static string ManualDownloadButtonComplete; + + public static void LoadEnglish() { + // Timeline/Events + TimelineTitle = "Timeline"; + SuggestionsTitle = "Suggestions"; + MessageCompleteRegistration = "Complete registration before using Hot Reload"; + MessageUseStartButton = "Use the Start button to activate Hot Reload"; + MessageEnableFilters = "Enable filters to see events"; + MessageMakeCodeChanges = "Make code changes to see events"; + MessageOnly40EntriesShown = "Only last 40 entries are shown"; + EventsTooltip = "Events"; + LabelSuggestionsFormat = "Suggestions ({0})"; + LabelTimeline = "Timeline"; + ButtonIgnoreEventType = "Ignore this event type "; + MessageStartHotReload = "Press Start to begin using Hot Reload"; + + // Partially Supported Change Descriptions + PartiallySupportedLambdaClosure = "A lambda closure was edited (captured variable was added or removed). Changes to it will only be visible to the next created lambda(s)."; + PartiallySupportedEditAsyncMethod = "An async method was edited. Changes to it will only be visible the next time this method is called."; + PartiallySupportedAddMonobehaviourMethod = "A new method was added or made public. It will not show up in the Inspector until the next full recompilation."; + PartiallySupportedEditMonobehaviourField = "A field in a MonoBehaviour was removed or reordered. The inspector will not notice this change until the next full recompilation."; + PartiallySupportedEditCoroutine = "An IEnumerator/IEnumerable was edited. When used as a coroutine, changes to it will only be visible the next time the coroutine is created."; + PartiallySupportedEditGenericFieldInitializer = "A field initializer inside generic class was edited. Field initializer will not have any effect until the next full recompilation."; + PartiallySupportedAddEnumMember = "An enum member was added. ToString and other reflection methods work only after the next full recompilation. Additionally, changes to the enum order may not apply until you patch usages in other places of the code."; + PartiallySupportedEditFieldInitializer = "A field initializer was edited. Changes will only apply to new instances of that type, since the initializer for an object only runs when it is created."; + PartiallySupportedAddMethodWithAttributes = "A method with attributes was added. Method attributes will not have any effect until the next full recompilation."; + PartiallySupportedAddFieldWithAttributes = "A field with attributes was added. Field attributes will not have any effect until the next full recompilation."; + PartiallySupportedGenericMethodInGenericClass = "A generic method was edited. Usages in non-generic classes applied, but usages in the generic classes are not supported."; + PartiallySupportedNewCustomSerializableField = "A new custom serializable field was added. The inspector will not notice this change until the next full recompilation."; + PartiallySupportedMultipleFieldsEditedInTheSameType = "Multiple fields modified in the same type during a single patch. Their values have been reset."; + + // Event Entry Titles and Descriptions + EventTitleFailedApplyingPatch = "Failed applying patch to method"; + EventDescriptionInlinedMethods = "Some methods got inlined by the Unity compiler and cannot be patched by Hot Reload. Switch to Debug mode to avoid this problem."; + EventDescriptionNoIssuesFound = "No issues found"; + EventDescriptionSeeUnsupportedChangesBelow = "See unsupported changes below"; + EventDescriptionSeeDetailedEntriesBelow = "See detailed entries below"; + EventDescriptionSeePartiallyAppliedChangesBelow = "See partially applied changes below"; + EventDescriptionUndetectedChange = "Code semantics didn't change (e.g. whitespace) or the change requires manual recompile.\n\nRecompile to force-apply changes."; + EventTitleChangePartiallyApplied = "Change partially applied"; + EventDescriptionFailedApplyingPatchTapForMore = "{0}: {1}, tap here to see more."; + + // Manual download + ManualDownloadButtonContactSupport = "Contact Support"; + ManualDownloadWarning = "Automatic downloads are slow or not available. You can download the required binaries manually."; + ManualDownloadInfo = "Download the server binary and save it to the following path:\n{0}"; + ManualDownloadButtonCopyToClipboard = "Copy path to clipboard"; + ManualDownloadButtonOpenDownloadUrl = "Open Download URL"; + ManualDownloadButtonComplete = "I've completed the download"; + } + + public static void LoadSimplifiedChinese() { + // Timeline/Events + TimelineTitle = "时间线"; + SuggestionsTitle = "建议"; + MessageCompleteRegistration = "在使用 Hot Reload 前完成注册"; + MessageUseStartButton = "使用开始按钮激活 Hot Reload"; + MessageEnableFilters = "启用过滤器以查看事件"; + MessageMakeCodeChanges = "进行代码更改以查看事件"; + MessageOnly40EntriesShown = "仅显示最后 40 个条目"; + EventsTooltip = "事件"; + LabelSuggestionsFormat = "建议 ({0})"; + LabelTimeline = "时间线"; + ButtonIgnoreEventType = "忽略此事件类型 "; + MessageStartHotReload = "按开始以开始使用 Hot Reload"; + + // Partially Supported Change Descriptions + PartiallySupportedLambdaClosure = "编辑了 lambda 闭包(添加或删除了捕获的变量)。对其的更改仅对下一个创建的 lambda 可见。"; + PartiallySupportedEditAsyncMethod = "编辑了异步方法。对其的更改仅在下次调用此方法时可见。"; + PartiallySupportedAddMonobehaviourMethod = "添加了新方法或将其设为公共。在下次完全重新编译之前,它不会显示在检查器中。"; + PartiallySupportedEditMonobehaviourField = "删除了 MonoBehaviour 中的字段或重新排序。在下次完全重新编译之前,检查器不会注意到此更改。"; + PartiallySupportedEditCoroutine = "编辑了 IEnumerator/IEnumerable。当用作协程时,对其的更改仅在下次创建协程时可见。"; + PartiallySupportedEditGenericFieldInitializer = "编辑了泛型类中的字段初始化器。在下次完全重新编译之前,字段初始化器不会有任何效果。"; + PartiallySupportedAddEnumMember = "添加了枚举成员。ToString 和其他反射方法仅在下次完全重新编译后才起作用。此外,在您修补代码中其他地方的用法之前,对枚举顺序的更改可能不会应用。"; + PartiallySupportedEditFieldInitializer = "编辑了字段初始化器。更改仅适用于该类型的新实例,因为对象的初始化器仅在创建时运行。"; + PartiallySupportedAddMethodWithAttributes = "添加了带属性的方法。在下次完全重新编译之前,方法属性不会有任何效果。"; + PartiallySupportedAddFieldWithAttributes = "添加了带属性的字段。在下次完全重新编译之前,字段属性不会有任何效果。"; + PartiallySupportedGenericMethodInGenericClass = "编辑了泛型方法。在非泛型类中的用法已应用,但不支持在泛型类中的用法。"; + PartiallySupportedNewCustomSerializableField = "添加了新的自定义可序列化字段。在下次完全重新编译之前,检查器不会注意到此更改。"; + PartiallySupportedMultipleFieldsEditedInTheSameType = "在单个补丁期间,在同一类型中修改了多个字段。它们的值已被重置。"; + + // Event Entry Titles and Descriptions + EventTitleFailedApplyingPatch = "将补丁应用于方法失败"; + EventDescriptionInlinedMethods = "某些方法被 Unity 编译器内联,无法被 Hot Reload 修补。切换到调试模式以避免此问题。"; + EventDescriptionNoIssuesFound = "未发现问题"; + EventDescriptionSeeUnsupportedChangesBelow = "请参阅下面的不支持的更改"; + EventDescriptionSeeDetailedEntriesBelow = "请参阅下面的详细条目"; + EventDescriptionSeePartiallyAppliedChangesBelow = "请参阅下面部分应用的更改"; + EventDescriptionUndetectedChange = "代码语义未更改(例如空格)或更改需要手动重新编译。\n\n重新编译以强制应用更改。"; + EventTitleChangePartiallyApplied = "更改部分应用"; + EventDescriptionFailedApplyingPatchTapForMore = "{0}: {1},点击此处查看更多。"; + + // Manual download + ManualDownloadButtonContactSupport = "联系支持"; + ManualDownloadWarning = "自动下载速度较慢或不可用。您可以手动下载所需的二进制文件。"; + ManualDownloadInfo = "下载服务器二进制文件并将其保存到以下路径:\n{0}"; + ManualDownloadButtonCopyToClipboard = "复制路径到剪贴板"; + ManualDownloadButtonOpenDownloadUrl = "打开下载链接"; + ManualDownloadButtonComplete = "我已完成下载"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs.meta new file mode 100644 index 0000000..c260ad0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: f7d83a43b1da0f14c860b06211f42149 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Localization/TimelineTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs new file mode 100644 index 0000000..5d2a12c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs @@ -0,0 +1,84 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class UI { + // Run Tab + public static string RunTabTitle; + public static string RunTabTooltip; + public static string TapToShowStacktrace; + public static string CompileErrorMessage; + public static string UnsupportedChangeMessage; + public static string TapHereToSeeMore; + public static string ClickableDescription; + public static string SessionRefreshString; + public static string RecompileButtonLabel; + public static string StartButtonLabel; + public static string StopButtonLabel; + + // License Messages + public static string TrialLicenseMessage; + public static string IndieLicenseMessage; + public static string LicenseRenewalMessage; + public static string BusinessLicenseMessage; + + // Startup Messages + public static string StartingHotReloadMessage; + + public static string OverlayPanelName; + + public static void LoadEnglish() { + // Run Tab + RunTabTitle = "Run"; + RunTabTooltip = "Run and monitor the current Hot Reload session."; + TapToShowStacktrace = "Tap to show stacktrace"; + CompileErrorMessage = "Compile error"; + UnsupportedChangeMessage = "Unsupported change detected"; + TapHereToSeeMore = "tap here to see more."; + ClickableDescription = "Unsupported change"; + SessionRefreshString = "Next Session: {0}h {1}min"; + RecompileButtonLabel = " Recompile"; + StartButtonLabel = " Start"; + StopButtonLabel = " Stop"; + + // License Messages + TrialLicenseMessage = "Using Trial license, valid until {0}"; + IndieLicenseMessage = " Indie license active"; + LicenseRenewalMessage = "License will renew on {0}."; + BusinessLicenseMessage = " Business license active"; + + // Startup Messages + StartingHotReloadMessage = "Starting Hot Reload"; + + // Overlay + OverlayPanelName = "Hot Reload Indication"; + } + + public static void LoadSimplifiedChinese() { + // Run Tab + RunTabTitle = "运行"; + RunTabTooltip = "运行并监控当前的 Hot Reload 会话。"; + TapToShowStacktrace = "点击以显示堆栈跟踪"; + CompileErrorMessage = "编译错误"; + UnsupportedChangeMessage = "检测到不支持的更改"; + TapHereToSeeMore = "点击此处查看更多。"; + ClickableDescription = "不支持的更改"; + SessionRefreshString = "下一会话:{0}h {1}min"; + RecompileButtonLabel = " 重新编译"; + StartButtonLabel = " 开始"; + StopButtonLabel = " 停止"; + + // License Messages + TrialLicenseMessage = "正在使用试用许可证,有效期至 {0}"; + IndieLicenseMessage = " 独立开发者许可证已激活"; + LicenseRenewalMessage = "许可证将于 {0} 续订。"; + BusinessLicenseMessage = " 商业许可证已激活"; + + // Startup Messages + StartingHotReloadMessage = "正在启动 Hot Reload"; + + // Overlay + OverlayPanelName = "Hot Reload 指示"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs.meta new file mode 100644 index 0000000..815b133 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/UITranslations.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 84e781692dea7c94f8da7bd64dfbfceb +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/UITranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs b/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs new file mode 100644 index 0000000..ade24c1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs @@ -0,0 +1,113 @@ +namespace SingularityGroup.HotReload.Editor.Localization { + internal static partial class Translations { + public static class Utility { + // Compilation and Assembly + public static string CompileError; + public static string UnsupportedChange; + public static string AssemblyFileEditError; + public static string NativePluginEditError; + public static string InspectorFieldChangeError; + + // Version and Project + public static string InvalidVersionNoMinor; + public static string InvalidVersionNoPatch; + public static string FailedCreateCSharpProject; + public static string ApplicationNotFound; + + // Download and Installation + public static string StreamHasToBeReadable; + public static string StreamHasToBeWritable; + public static string UnableToLocateServer; + public static string UnableToLocateServerDetail; + public static string CannotFindSolutionFile; + public static string UnableToUpdatePackageDifferentDrive; + public static string UnableToLocateHotReloadPackage; + + // CLI and Build + public static string FailedLocatingServer; + public static string PreparingBuildInfoFailed; + + // Logs + public static string PlayerAssemblyDebug; + + // Symbols + public static string GenericParameterMismatch; + public static string GenericParameterTypeDefinitionMismatch; + + // Method Compatibility + public static string MethodCallWarning; + public static string OnHotReloadLocalWarning; + public static string OnHotReloadWarning; + public static string OnHotReloadLocalCallWarning; + + public static void LoadEnglish() { + CompileError = "Compile error"; + UnsupportedChange = "Unsupported change"; + AssemblyFileEditError = "errors: AssemblyFileEdit: Editing assembly files requires recompiling in Unity. in {0}"; + NativePluginEditError = "errors: NativePluginEdit: Editing native plugins requires recompiling in Unity. in {0}"; + InspectorFieldChangeError = "errors: Some inspector field changes require recompilation in Unity. Auto recompiling Unity according to the settings."; + + InvalidVersionNoMinor = "Invalid version (no minor version given in strict mode)"; + InvalidVersionNoPatch = "Invalid version (no patch version given in strict mode)"; + FailedCreateCSharpProject = "Failed creating c# project because the c# project header did not have the correct amount of arguments, which is {0}"; + ApplicationNotFound = "Application not found"; + + StreamHasToBeReadable = "Has to be readable"; + StreamHasToBeWritable = "Has to be writable"; + UnableToLocateServer = "Unable to locate the 'Server' directory. "; + UnableToLocateServerDetail = "Make sure the 'Server' directory is somewhere in the Assets folder inside a 'HotReload' folder or in the HotReload package"; + CannotFindSolutionFile = "Cannot find solution file. Please disable \"useBuiltInProjectGeneration\" in settings to enable custom project generation."; + UnableToUpdatePackageDifferentDrive = "unable to update package because it is located on a different drive than the unity project"; + UnableToLocateHotReloadPackage = "unable to locate hot reload package"; + + FailedLocatingServer = "Failed to locate Hot Reload server directory"; + PreparingBuildInfoFailed = "Preparing build info failed! On-device functionality might not work. Exception: {0}"; + + PlayerAssemblyDebug = "player assembly named {0}"; + + GenericParameterMismatch = "Generic parameter did not resolve to generic type definition"; + GenericParameterTypeDefinitionMismatch = "Generic parameter did not exist on the generic type definition"; + + MethodCallWarning = "failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour."; + OnHotReloadLocalWarning = "failed to find method {0}. Make sure it exists within the same class."; + OnHotReloadWarning = "failed. Make sure it has 0 parameters, or 1 parameter with type List. Exception:"; + OnHotReloadLocalCallWarning = "failed. Make sure it has 0 parameters. Exception:"; + } + + public static void LoadSimplifiedChinese() { + CompileError = "编译错误"; + UnsupportedChange = "不支持的更改"; + AssemblyFileEditError = "错误:AssemblyFileEdit:编辑程序集文件需要在 Unity 中重新编译。在 {0} 中"; + NativePluginEditError = "错误:NativePluginEdit:编辑本机插件需要在 Unity 中重新编译。在 {0} 中"; + InspectorFieldChangeError = "错误:一些检查器字段更改需要在 Unity 中重新编译。根据设置自动重新编译 Unity。"; + + InvalidVersionNoMinor = "无效版本(严格模式下未提供次要版本)"; + InvalidVersionNoPatch = "无效版本(严格模式下未提供补丁版本)"; + FailedCreateCSharpProject = "创建 C# 项目失败,因为 C# 项目头没有正确的参数数量,即 {0}"; + ApplicationNotFound = "未找到应用程序"; + + StreamHasToBeReadable = "必须可读"; + StreamHasToBeWritable = "必须可写"; + UnableToLocateServer = "无法找到“服务器”目录。"; + UnableToLocateServerDetail = "确保“服务器”目录位于 Assets 文件夹内的“HotReload”文件夹中,或在 HotReload 包中"; + CannotFindSolutionFile = "找不到解决方案文件。请在设置中禁用“useBuiltInProjectGeneration”以启用自定义项目生成。"; + UnableToUpdatePackageDifferentDrive = "无法更新包,因为它位于与 unity 项目不同的驱动器上"; + UnableToLocateHotReloadPackage = "无法找到 hot reload 包"; + + FailedLocatingServer = "未能找到 Hot Reload 服务器目录"; + PreparingBuildInfoFailed = "准备构建信息失败!设备上功能可能无法工作。异常:{0}"; + + PlayerAssemblyDebug = "名为 {0} 的播放器程序集"; + + GenericParameterMismatch = "泛型参数未解析为泛型类型定义"; + GenericParameterTypeDefinitionMismatch = "泛型参数在泛型类型定义上不存在"; + + MethodCallWarning = "失败。请确保它是一个具有 0 个参数的方法,静态或在 MonoBehaviour 上定义。"; + OnHotReloadLocalWarning = "未能找到方法 {0}。请确保它存在于同一个类中。"; + OnHotReloadWarning = "失败。请确保它有 0 个参数,或 1 个类型为 List 的参数。异常:"; + OnHotReloadLocalCallWarning = "失败。请确保它有 0 个参数。异常:"; + } + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs.meta new file mode 100644 index 0000000..d74074a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Localization/UtilityTranslations.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c089e14ef1f6aa5438ecc4ea082be7d9 +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/UtilityTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild.meta new file mode 100644 index 0000000..d600ba4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0fe483b6b7ad4be79b58901d03e35511 +timeCreated: 1674041345 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs new file mode 100644 index 0000000..6c66c53 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using UnityEditor; +using UnityEditor.Build; + +#pragma warning disable CS0618 +namespace SingularityGroup.HotReload.Editor { + public class BuildGenerateBuildInfo : IPreprocessBuild, IPostprocessBuild { + public int callbackOrder => 10; + + public void OnPreprocessBuild(BuildTarget target, string path) { + try { + if (!HotReloadBuildHelper.IncludeInThisBuild() || MultiplayerPlaymodeHelper.IsClone) { + return; + } + // write BuildInfo json into the StreamingAssets directory + GenerateBuildInfo(BuildInfo.GetStoredPath(), target); + } catch (BuildFailedException) { + throw; + } catch (Exception e) { + throw new BuildFailedException(e); + } + } + + private static void GenerateBuildInfo(string buildFilePath, BuildTarget buildTarget) { + var buildInfo = BuildInfoHelper.GenerateBuildInfoMainThread(buildTarget); + // write to StreamingAssets + // create StreamingAssets folder if not exists (in-case project has no StreamingAssets files) + // ReSharper disable once AssignNullToNotNullAttribute + Directory.CreateDirectory(Path.GetDirectoryName(buildFilePath)); + File.WriteAllText(buildFilePath, buildInfo.ToJson()); + } + + public void OnPostprocessBuild(BuildTarget target, string path) { + try { + File.Delete(BuildInfo.GetStoredPath()); + } catch { + // ignore + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs.meta new file mode 100644 index 0000000..de4a763 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 178df48ca88b4cddac448a49196b49bf +timeCreated: 1682338738 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs new file mode 100644 index 0000000..c767fd8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + internal static class HotReloadBuildHelper { + /// + /// Should HotReload runtime be included in the current build? + /// + public static bool IncludeInThisBuild() { + return IsAllBuildSettingsSupported(); + } + + /// + /// Get scripting backend for the current platform. + /// + /// Scripting backend + public static ScriptingImplementation GetCurrentScriptingBackend() { +#pragma warning disable CS0618 + return PlayerSettings.GetScriptingBackend(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); +#pragma warning restore CS0618 + } + + public static ManagedStrippingLevel GetCurrentStrippingLevel() { +#pragma warning disable CS0618 + return PlayerSettings.GetManagedStrippingLevel(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); +#pragma warning restore CS0618 + } + + public static void SetCurrentScriptingBackend(ScriptingImplementation to) { +#pragma warning disable CS0618 + // only set it if default is not correct (avoid changing ProjectSettings when not needed) + if (GetCurrentScriptingBackend() != to) { + PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, to); + } +#pragma warning restore CS0618 + } + + public static void SetCurrentStrippingLevel(ManagedStrippingLevel to) { +#pragma warning disable CS0618 + // only set it if default is not correct (avoid changing ProjectSettings when not needed) + if (GetCurrentStrippingLevel() != to) { + PlayerSettings.SetManagedStrippingLevel(EditorUserBuildSettings.selectedBuildTargetGroup, to); + } +#pragma warning restore CS0618 + } + + /// Is the current build target supported? + /// main thread only + public static bool IsBuildTargetSupported() { + var buildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; + return Array.IndexOf(unsupportedBuildTargets, buildTarget) == -1; + } + + /// Are all the settings supported? + /// main thread only + static bool IsAllBuildSettingsSupported() { + if (!IsBuildTargetSupported()) { + return false; + } + + // need way to give it settings object, dont want to give serializedobject + var options = HotReloadSettingsEditor.LoadSettingsOrDefault(); + var so = new SerializedObject(options); + + // check all projeect options + foreach (var option in HotReloadSettingsTab.allOptions) { + var projectOption = option as ProjectOptionBase; + if (projectOption != null) { + // if option is required, build can't use hot reload + if (projectOption.IsRequiredForBuild() && !projectOption.GetValue(so)) { + return false; + } + } + } + + return GetCurrentScriptingBackend() == ScriptingImplementation.Mono2x + && GetCurrentStrippingLevel() == ManagedStrippingLevel.Disabled + && EditorUserBuildSettings.development; + } + + /// + /// Some platforms are not supported because they don't have Mono scripting backend. + /// + /// + /// Only list the platforms that definately don't have Mono scripting. + /// + private static readonly BuildTargetGroup[] unsupportedBuildTargets = new [] { + BuildTargetGroup.iOS, // mono support was removed many years ago + BuildTargetGroup.WebGL, // has never had mono + }; + +#pragma warning disable CS0618 + public static bool IsMonoSupported(BuildTargetGroup buildTarget) { + var backend = PlayerSettings.GetScriptingBackend(buildTarget); + try { + // GetDefaultScriptingBackend returns IL2CPP for Unity 6 which goes against Unity documentation. + // Have to use a workaround approach instead + PlayerSettings.SetScriptingBackend(buildTarget, ScriptingImplementation.Mono2x); + return PlayerSettings.GetScriptingBackend(buildTarget) == ScriptingImplementation.Mono2x; + } catch { + return false; + } finally { + PlayerSettings.SetScriptingBackend(buildTarget, backend); + } + } +#pragma warning restore CS0618 + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs.meta new file mode 100644 index 0000000..c6c6979 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b9aa611f02544b609c5b29f9d1409d6e +timeCreated: 1674041425 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs new file mode 100644 index 0000000..18b83fd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor.Android; +using UnityEditor.Build; + +namespace SingularityGroup.HotReload.Editor { +#pragma warning disable CS0618 + /// + /// + /// This class sets option in the AndroidManifest that you choose in HotReload build settings. + /// + /// + /// - To connect to the HotReload server through the local network, we need to permit access to http://192...
+ /// - Starting with Android 9, insecure http requests are not allowed by default and must be whitelisted + ///
+ ///
+ internal class PostbuildModifyAndroidManifest : IPostGenerateGradleAndroidProject { +#pragma warning restore CS0618 + public int callbackOrder => 10; + + private const string manifestFileName = "AndroidManifest.xml"; + + public void OnPostGenerateGradleAndroidProject(string path) { + try { + if (!HotReloadBuildHelper.IncludeInThisBuild()) { + return; + } + // Note: in future we may support users with custom configuration for usesCleartextTraffic + #if UNITY_2022_1_OR_NEWER + // Unity 2022 or newer → do nothing, we rely on Unity option to control the flag + #else + // Unity 2021 or older → put manifest flag in if Unity is making a Development Build + var manifestFilePath = FindAndroidManifest(path); + if (manifestFilePath == null) { + throw new BuildFailedException(string.Format(Translations.Errors.ExceptionUnableToFindManifest, CodePatcher.TAG, manifestFileName)); + } + SetUsesCleartextTraffic(manifestFilePath); + #endif + } catch (BuildFailedException) { + throw; + } catch (Exception e) { + throw new BuildFailedException(e); + } + } + + /// identifier that is used in the deeplink uri scheme + /// (initially tried Application.identifier, but that was giving unexpected results based on PlayerSettings) + // SG-29580 + // Something to uniqly identify the application, but it must be something which is highly likely + // to be the same at build time (studio might have logic to set e.g. product name to MyGameProd or MyGameTest) + public static string ApplicationIdentiferSlug => "app"; +/* + public static string ApplicationIdentiferSlug => Regex.Replace(ApplicationIdentifer, @"[^a-zA-Z0-9\.\-]", "") + .Replace("..", ".") // happens if your companyname in Unity ends with a dot + .ToLowerInvariant(); + + private static void AddDeeplinkForwarder(string manifestFilePath) { + // add the hotreload-${identifier} uri scheme to the AndroidManifest.xml file + // it should be added as part of an intent-filter for the activity "com.singularitygroup.deeplinkforwarder.DeepLinkForwarderActivity" + var contents = File.ReadAllText(manifestFilePath); + if (contents.Contains("android:name=\"com.singularitygroup.deeplinkforwarder.DeepLinkForwarderActivity\"")) { + // user has already set this themselves, don't replace it + return; + } + + //note: not using android:host or any other data attr because android still shows a chooser for all ur hotreload apps + // Therefore must use a unique uri scheme to ensure only one app can handle it. + var activityWithIntentFilter = @" + + + + + + + +"; + var newContents = Regex.Replace(contents, + @"", + activityWithIntentFilter + "\n " + ); + File.WriteAllText(manifestFilePath, newContents); + } +*/ + // Assume unityLibraryPath is to {gradleProject}/unityLibrary/ which is roughly the same across Unity versions 2018/2019/2020/2021/2022 + private static string FindAndroidManifest(string unityLibraryPath) { + // find the AndroidManifest.xml file which we can edit + var dir = new DirectoryInfo(unityLibraryPath); + var manifestFilePath = Path.Combine(dir.FullName, "src", "main", manifestFileName); + if (File.Exists(manifestFilePath)) { + return manifestFilePath; + } + + Log.Info(Translations.Errors.InfoManifestSearch, manifestFileName, manifestFilePath, dir.FullName); + var manifestFiles = dir.GetFiles(manifestFileName, SearchOption.AllDirectories); + if (manifestFiles.Length == 0) { + return null; + } + + foreach (var file in manifestFiles) { + if (file.FullName.Contains("src")) { + // good choice + return file.FullName; + } + } + // fallback to the first file found + return manifestFiles[0].FullName; + } + + /// + /// Set option android:usesCleartextTraffic="true" + + /// + /// Absolute filepath to the unityLibrary AndroidManifest.xml file + private static void SetUsesCleartextTraffic(string manifestFilePath) { + // Ideally we would create or modify a "Network Security Configuration file" to permit access to local ip addresses + // https://developer.android.com/training/articles/security-config#manifest + // but that becomes difficult when the user has their own configuration file - would need to search for it and it may be inside an aar. + var contents = File.ReadAllText(manifestFilePath); + if (contents.Contains("android:usesCleartextTraffic=")) { + // user has already set this themselves, don't replace it + return; + } + var newContents = Regex.Replace(contents, + @""; + newContents += $"\n"; + File.WriteAllText(manifestFilePath, newContents); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs.meta new file mode 100644 index 0000000..59a2887 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 1949292efc07445ea4c040d544e2d369 +timeCreated: 1675441886 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs new file mode 100644 index 0000000..fda1bee --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs @@ -0,0 +1,26 @@ +using System; +using SingularityGroup.HotReload.Editor.Cli; +using UnityEditor; +using UnityEditor.Build; + +namespace SingularityGroup.HotReload.Editor { +#pragma warning disable CS0618 + class PostbuildSendProjectState : IPostprocessBuild { +#pragma warning restore CS0618 + public int callbackOrder => 9999; + public void OnPostprocessBuild(BuildTarget target, string path) { + try { + if (!HotReloadBuildHelper.IncludeInThisBuild() || MultiplayerPlaymodeHelper.IsClone) { + return; + } + // after build passes, need to send again because EditorApplication.delayCall isn't called. + var buildInfo = BuildInfoHelper.GenerateBuildInfoMainThread(); + HotReloadCli.PrepareBuildInfo(buildInfo); + } catch (BuildFailedException) { + throw; + } catch (Exception e) { + throw new BuildFailedException(e); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs.meta new file mode 100644 index 0000000..2c54056 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3b27b9eab16f78f448477e546fd5eb97 +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/PlayerBuild/PostbuildSendProjectState.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs new file mode 100644 index 0000000..bddec76 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs @@ -0,0 +1,64 @@ +using System; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; +using SingularityGroup.HotReload.Editor.Localization; + +namespace SingularityGroup.HotReload.Editor { + /// Includes HotReload Resources only in development builds + /// + /// This build script ensures that HotReload Resources are not included in release builds. + /// + /// When HotReload is enabled:
+ /// - include HotReloadSettingsObject in development Android builds.
+ /// - exclude HotReloadSettingsObject from the build.
+ /// When HotReload is disabled:
+ /// - excludes HotReloadSettingsObject from the build.
+ ///
+ ///
+#pragma warning disable CS0618 + internal class PrebuildIncludeResources : IPreprocessBuild, IPostprocessBuild { +#pragma warning restore CS0618 + public int callbackOrder => 10; + + // Preprocess warnings don't show up in console + bool warnSettingsNotSupported; + + public void OnPreprocessBuild(BuildTarget target, string path) { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + try { + if (HotReloadBuildHelper.IncludeInThisBuild()) { + // move scriptable object into Resources/ folder + HotReloadSettingsEditor.AddOrRemoveFromBuild(true); + } else { + // make sure HotReload resources are not in the build + HotReloadSettingsEditor.AddOrRemoveFromBuild(false); + + var options = HotReloadSettingsEditor.LoadSettingsOrDefault(); + var so = new SerializedObject(options); + if (IncludeInBuildOption.I.GetValue(so)) { + warnSettingsNotSupported = true; + } + } + } catch (BuildFailedException) { + throw; + } catch (Exception ex) { + throw new BuildFailedException(ex); + } + } + + public void OnPostprocessBuild(BuildTarget target, string path) { + if (warnSettingsNotSupported) { + Debug.LogWarning(Translations.Errors.WarningBuildSettingsNotSupported); + } + } + + // Do nothing in post build. settings asset will be dirty if build fails, so not worth fixing just for successful builds. + // [PostProcessBuild] + // private static void PostBuild(BuildTarget target, string pathToBuiltProject) { + // } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs.meta new file mode 100644 index 0000000..46a6c16 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7a63dd2d10359e94a8a3c24bf59164fb +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/PlayerBuild/PrebuildIncludeResources.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration.meta new file mode 100644 index 0000000..f6f2a3a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac3ce0c4b45643859281090bdc74719c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs new file mode 100644 index 0000000..2af8f5a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using System.Security; +using System.Text; +using SingularityGroup.HotReload.Editor.Util; + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration { + class FileIOProvider : IFileIO + { + public bool Exists(string fileName) + { + return File.Exists(fileName); + } + + public string ReadAllText(string fileName) + { + return File.ReadAllText(fileName); + } + + public void WriteAllText(string path, string content) + { + File.WriteAllText(path, content, Encoding.UTF8); + } + + public string EscapedRelativePathFor(string file, string projectDirectory) + { + var projectDir = Path.GetFullPath(projectDirectory); + + // We have to normalize the path, because the PackageManagerRemapper assumes + // dir seperators will be os specific. + var absolutePath = Path.GetFullPath(file.NormalizePath()); + var path = SkipPathPrefix(absolutePath, projectDir); + + return SecurityElement.Escape(path); + } + + private static string SkipPathPrefix(string path, string prefix) + { + return path.StartsWith($@"{prefix}{Path.DirectorySeparatorChar}", StringComparison.Ordinal) + ? path.Substring(prefix.Length + 1) + : path; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs.meta new file mode 100644 index 0000000..5c49367 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 27e102e0bda3b0c4f95d4e5e9e3bf16a +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/ProjectGeneration/FileIOProvider.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs new file mode 100644 index 0000000..d55e241 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs @@ -0,0 +1,9 @@ +namespace SingularityGroup.HotReload.Editor.ProjectGeneration { + class GUIDProvider : IGUIDGenerator + { + public string ProjectGuid(string name) + { + return SolutionGuidGenerator.GuidForProject(name); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs.meta new file mode 100644 index 0000000..062ba14 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ca39683512eedae448726b029ca101ec +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/ProjectGeneration/GUIDProvider.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs new file mode 100644 index 0000000..0cda7f5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs @@ -0,0 +1,12 @@ +namespace SingularityGroup.HotReload.Editor.ProjectGeneration +{ + internal interface IFileIO + { + bool Exists(string fileName); + + string ReadAllText(string fileName); + void WriteAllText(string path, string content); + + string EscapedRelativePathFor(string file, string projectDirectory); + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs.meta new file mode 100644 index 0000000..2f8dc0c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6e856fe8c0b7d3c45a06140d120fdd87 +timeCreated: 1580717666 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs new file mode 100644 index 0000000..46bde64 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs @@ -0,0 +1,7 @@ +namespace SingularityGroup.HotReload.Editor.ProjectGeneration +{ + internal interface IGUIDGenerator + { + string ProjectGuid(string name); + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs.meta new file mode 100644 index 0000000..fef65be --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 27fd74267707df04ab2cfac0e8abd782 +timeCreated: 1580717700 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs new file mode 100644 index 0000000..de473c0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs @@ -0,0 +1,43 @@ +namespace SingularityGroup.HotReload.Editor.ProjectGeneration { + /// + /// Allows to post process Hot Reload's project generation. + /// This should only be needed if you tinker with Unity's project generation as well. + /// Types that inherit from this interface will get created automatically whenever Hot Reload generates project files. + /// Types that implement this interface need to have a public parameterless default constructor. + /// + public interface IHotReloadProjectGenerationPostProcessor { + + /// + /// Specifies the ordering of the post processor. + /// Post processors with lower callback order get executed first. + /// + int CallbackOrder { get; } + + /// + /// Use this method to set up state you need for the project generation. + /// Calls to unity API need to happen here and it's values need to be cached. + /// This is the only method that will get executed on the main thread. + /// + void InitializeOnMainThread(); + + /// + /// Gets called whenever Hot Reload generated a project file. + /// The destination file path for the .csproj file + /// The file contents of the .csproj file + /// + string OnGeneratedCSProjectThreaded(string path, string contents); + + /// + /// Gets called whenever Hot Reload generated a solution file. + /// The destination file path for the .sln file + /// The file contents of the .sln file + /// + string OnGeneratedSlnSolutionThreaded(string path, string contents); + + /// + /// Gets called after Hot Reload project generation is finished. + /// + void OnGeneratedCSProjectFilesThreaded(); + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs.meta new file mode 100644 index 0000000..9609c26 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ff65ad426f484ad5bbd34fb8f9204c4d +timeCreated: 1676637309 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs new file mode 100644 index 0000000..62eda90 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs @@ -0,0 +1,17 @@ +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration { + class ProjectGenenerationPostProcessor : AssetPostprocessor { + // Called once before any generation of sln/csproj files happens, can return true to disable generation altogether + static bool OnPreGeneratingCSProjectFiles() { + if (MultiplayerPlaymodeHelper.IsClone) { + return false; + } + ProjectGeneration.GenerateSlnAndCsprojFiles(Application.dataPath).Forget(); + return false; + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs.meta new file mode 100644 index 0000000..89b7ff1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d58a4838109c4b31ac7f221547ad82e8 +timeCreated: 1676527868 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs new file mode 100644 index 0000000..c9086e4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs @@ -0,0 +1,899 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload; +using SingularityGroup.HotReload.Editor.Localization; +using SingularityGroup.HotReload.Editor.Util; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEditor.PackageManager; +using UnityEditorInternal; +using Assembly = UnityEditor.Compilation.Assembly; +using PackageInfo = UnityEditor.PackageManager.PackageInfo; +#if UNITY_2019_1_OR_NEWER +using System.Reflection; +#endif + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration { + class ProjectGeneration { + private enum ScriptingLanguage { + None, + CSharp + } + + [Serializable] + class Config { + public string projectExclusionRegex; + public HashSet projectBlacklist; + public HashSet polyfillSourceFiles; + public bool excludeAllAnalyzers; + public bool useBuiltInProjectGeneration; + } + + public static readonly string MSBuildNamespaceUri = "http://schemas.microsoft.com/developer/msbuild/2003"; + + /// + /// Map source extensions to ScriptingLanguages + /// + private static readonly Dictionary k_BuiltinSupportedExtensions = + new Dictionary { + { "cs", ScriptingLanguage.CSharp }, + { "uxml", ScriptingLanguage.None }, + { "uss", ScriptingLanguage.None }, + { "shader", ScriptingLanguage.None }, + { "compute", ScriptingLanguage.None }, + { "cginc", ScriptingLanguage.None }, + { "hlsl", ScriptingLanguage.None }, + { "glslinc", ScriptingLanguage.None }, + { "template", ScriptingLanguage.None }, + { "raytrace", ScriptingLanguage.None }, + { "json", ScriptingLanguage.None }, + { "rsp", ScriptingLanguage.None }, + { "asmdef", ScriptingLanguage.None }, + { "asmref", ScriptingLanguage.None }, + { "xaml", ScriptingLanguage.None }, + { "tt", ScriptingLanguage.None }, + { "t4", ScriptingLanguage.None }, + { "ttinclude", ScriptingLanguage.None } + }; + + private string m_SolutionProjectEntryTemplate = string.Join(Environment.NewLine, + @"Project(""{{{0}}}"") = ""{1}"", ""{2}"", ""{{{3}}}""", + @"EndProject").Replace(" ", "\t"); + + private string m_SolutionProjectConfigurationTemplate = string.Join(Environment.NewLine, + @" {{{0}}}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", + @" {{{0}}}.Debug|Any CPU.Build.0 = Debug|Any CPU").Replace(" ", "\t"); + + private string[] m_ProjectSupportedExtensions = new string[0]; + + private readonly string m_ProjectName; + private readonly string m_ProjectDirectory; + private readonly string m_SolutionDirectory; + private readonly IFileIO m_FileIOProvider; + private readonly IGUIDGenerator m_GUIDGenerator; + private static readonly SemaphoreSlim gate = new SemaphoreSlim(1, 1); + + private const string k_ToolsVersion = "4.0"; + private const string k_ProductVersion = "10.0.20506"; + private const string k_BaseDirectory = "."; + +#if !UNITY_2020_2_OR_NEWER + private const string k_TargetLanguageVersion = "latest"; +#endif + + // ReSharper disable once CollectionNeverUpdated.Local + private readonly Dictionary m_PackageInfoCache = + new Dictionary(); + + private Assembly[] m_AllEditorAssemblies; + + private Assembly[] m_AllPlayerAssemblies; + + private string[] m_AllAssetPaths; + + private string m_EngineAssemblyPath; + + private string m_EditorAssemblyPath; + + private bool m_SuppressCommonWarnings; + + private string m_FallbackRootNamespace; + + private IHotReloadProjectGenerationPostProcessor[] m_PostProcessors; + + + public static bool IsSyncing => gate.CurrentCount == 0; + + internal static readonly string tempDir = PackageConst.LibraryCachePath + "/Solution"; + public static string GetUnityProjectDirectory(string dataPath) => new DirectoryInfo(dataPath).Parent.FullName; + public static string GetSolutionFilePath(string dataPath) => Path.Combine(tempDir, Path.GetFileName(GetUnityProjectDirectory(dataPath)) + ".sln"); + + public static Task GenerateSlnAndCsprojFiles(string dataPath) { + if (MultiplayerPlaymodeHelper.IsClone) { + return Task.CompletedTask; + } + if (!IsSyncing) { + return GenerateAsync(dataPath); + } + return Task.CompletedTask; + } + + public static Task EnsureSlnAndCsprojFiles(string dataPath) { + if (MultiplayerPlaymodeHelper.IsClone) { + return Task.CompletedTask; + } + if (File.Exists(GetSolutionFilePath(dataPath))) { + return Task.CompletedTask; + } + + return GenerateAsync(dataPath); + } + + private static Task GenerateAsync(string dataPath) { + Directory.CreateDirectory(tempDir); + var gen = new ProjectGeneration(tempDir, GetUnityProjectDirectory(dataPath)); + return gen.Sync(); + } + + + public ProjectGeneration(string solutionDirectory, string unityProjectDirectory) { + m_ProjectDirectory = unityProjectDirectory; + m_SolutionDirectory = solutionDirectory; + m_ProjectName = Path.GetFileName(unityProjectDirectory); + m_FileIOProvider = new FileIOProvider(); + m_GUIDGenerator = new GUIDProvider(); + } + + private async Task Sync() { + await ThreadUtility.SwitchToThreadPool(); + var config = LoadConfig(); + if (config.useBuiltInProjectGeneration) { + return; + } + + await ThreadUtility.SwitchToMainThread(); + await gate.WaitAsync(); + try { + //Cache all data that is accessed via unity API on the unity main thread. + m_AllAssetPaths = AssetDatabase.GetAllAssetPaths(); + m_ProjectSupportedExtensions = EditorSettings.projectGenerationUserExtensions; + m_EngineAssemblyPath = InternalEditorUtility.GetEngineAssemblyPath(); + m_EditorAssemblyPath = InternalEditorUtility.GetEditorAssemblyPath(); + m_FallbackRootNamespace = EditorSettings.projectGenerationRootNamespace; + m_SuppressCommonWarnings = +#if UNITY_2020_1_OR_NEWER + PlayerSettings.suppressCommonWarnings; +#else + false; +#endif + + //Do the remaining work on a separate thread + await Task.WhenAll( + BuildPackageInfoCache(), + BuildEditorAssemblies(), + BuildPostProcessors() + ); + await GenerateAndWriteSolutionAndProjects(config); + } finally { + gate.Release(); + } + } + + private Config LoadConfig() { + var configPath = Path.Combine(m_ProjectDirectory, PackageConst.ConfigFileName); + Config config; + if(File.Exists(configPath)) { + config = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); + } else { + config = new Config(); + } + return config; + } + + private bool ShouldFileBePartOfSolution(string file) { + // Exclude files coming from packages except if they are internalized. + if (IsInternalizedPackagePath(file)) { + return false; + } + + return HasValidExtension(file); + } + + private bool HasValidExtension(string file) { + var extension = Path.GetExtension(file); + + // Dll's are not scripts but still need to be included.. + if (file.Equals(".dll", StringComparison.OrdinalIgnoreCase)) + return true; + + return IsSupportedExtension(extension); + } + + private bool IsSupportedExtension(string extension) { + extension = extension.TrimStart('.'); + return k_BuiltinSupportedExtensions.ContainsKey(extension) || m_ProjectSupportedExtensions.Contains(extension); + } + + async Task GenerateAndWriteSolutionAndProjects(Config config) { + await ThreadUtility.SwitchToThreadPool(); + + var projectExclusionRegex = config.projectExclusionRegex != null ? new Regex(config.projectExclusionRegex, RegexOptions.Compiled | RegexOptions.Singleline) : null; + var projectBlacklist = config.projectBlacklist ?? new HashSet(); + var polyfillSourceFiles = config.polyfillSourceFiles ?? new HashSet(); + var filteredProjects = new HashSet(); + var runtimeDependenciesBuilder = new List(); + runtimeDependenciesBuilder.Add(typeof(HarmonyLib.DetourApi).Assembly.Location); +# if UNITY_2019_4_OR_NEWER + runtimeDependenciesBuilder.Add(typeof(Helper2019).Assembly.Location); +#endif +# if UNITY_2020_3_OR_NEWER + runtimeDependenciesBuilder.Add(typeof(Helper2020).Assembly.Location); +#endif +# if UNITY_2022_2_OR_NEWER + runtimeDependenciesBuilder.Add(typeof(Helper2022).Assembly.Location); +#endif + var runtimeDependencies = runtimeDependenciesBuilder.ToArray(); + + // Only synchronize islands that have associated source files and ones that we actually want in the project. + // This also filters out DLLs coming from .asmdef files in packages. + var assemblies = GetAssemblies(ShouldFileBePartOfSolution).ToArray(); + var projectParts = new List(); + foreach (var assembly in assemblies) { + if(projectExclusionRegex != null && projectExclusionRegex.IsMatch(assembly.name)) { + filteredProjects.Add(assembly.name); + continue; + } + var part = new ProjectPart(assembly.name, assembly, "", m_FallbackRootNamespace, polyfillSourceFiles); + string projectPath; +# if (UNITY_2021_3_OR_NEWER) + projectPath = Path.GetRelativePath(m_ProjectDirectory, ProjectFile(part)).Replace('\\', '/'); +# else + projectPath = ProjectFile(part).Replace('\\', '/').Replace(m_ProjectDirectory.Replace('\\', '/'), ""); +#endif + if(projectBlacklist.Contains(projectPath)) { + filteredProjects.Add(assembly.name); + continue; + } + projectParts.Add(part); + } + + SyncSolution(projectParts.ToArray()); + + await ThreadUtility.SwitchToMainThread(); + var responseFiles = new List[projectParts.Count]; + for (var i = 0; i < projectParts.Count; i++) { + responseFiles[i] = projectParts[i].ParseResponseFileData(m_ProjectDirectory).ToList(); + } + + await ThreadUtility.SwitchToThreadPool(); + for (var i = 0; i < projectParts.Count; i++) { + SyncProject(projectParts[i], responseFiles[i], filteredProjects, runtimeDependencies, config); + } + + foreach (var pp in m_PostProcessors) { + try { + pp.OnGeneratedCSProjectFilesThreaded(); + } catch (Exception ex) { + Log.Warning(Translations.Errors.WarningPostProcessorException, pp, ex); + } + } + } + + private void SyncProject( + ProjectPart island, + List responseFileData, + HashSet filteredProjects, + string[] runtimeDependencies, + Config config) { + + SyncProjectFileIfNotChanged( + ProjectFile(island), + ProjectText(island, responseFileData, filteredProjects, runtimeDependencies, config)); + } + + private void SyncProjectFileIfNotChanged(string path, string newContents) { + foreach (var pp in m_PostProcessors) { + try { + newContents = pp.OnGeneratedCSProjectThreaded(path, newContents); + } catch (Exception ex) { + Log.Warning(Translations.Errors.WarningPostProcessorFailedProject, pp, path, ex); + } + } + + SyncFileIfNotChanged(path, newContents); + } + + private void SyncSolutionFileIfNotChanged(string path, string newContents) { + foreach (var pp in m_PostProcessors) { + try { + newContents = pp.OnGeneratedSlnSolutionThreaded(path, newContents); + } catch (Exception ex) { + Log.Warning(Translations.Errors.WarningPostProcessorFailedSolution, pp, path, ex); + } + } + + SyncFileIfNotChanged(path, newContents); + } + + + private void SyncFileIfNotChanged(string path, string newContents) { + try { + if (m_FileIOProvider.Exists(path) && newContents == m_FileIOProvider.ReadAllText(path)) { + return; + } + } catch (Exception exception) { + Log.Exception(exception); + } + + m_FileIOProvider.WriteAllText(path, newContents); + } + + private string ProjectText(ProjectPart assembly, List responseFilesData, HashSet filteredProjects, string[] runtimeDependencies, Config config) { + var projectBuilder = new StringBuilder(ProjectHeader(assembly, responseFilesData, config)); + + foreach (var file in assembly.SourceFiles) { + var fullFile = m_FileIOProvider.EscapedRelativePathFor(file, m_SolutionDirectory); + projectBuilder.Append(" ").Append(Environment.NewLine); + } + + projectBuilder.Append(assembly.AssetsProjectPart); + + var responseRefs = responseFilesData.SelectMany(x => x.FullPathReferences.Select(r => r)); + var internalAssemblyReferences = assembly.AssemblyReferences + .Where(reference => filteredProjects.Contains(reference.name) || !reference.sourceFiles.Any(ShouldFileBePartOfSolution)).Select(i => i.outputPath); + var allReferences = + assembly.CompiledAssemblyReferences + .Union(responseRefs) + .Union(internalAssemblyReferences).ToArray(); + + foreach (var reference in allReferences) { + var fullReference = Path.IsPathRooted(reference) ? reference : Path.Combine(m_ProjectDirectory, reference); + AppendReference(fullReference, projectBuilder); + } + foreach (var path in runtimeDependencies) { + AppendReference(path, projectBuilder); + } + + if (assembly.AssemblyReferences.Length > 0) { + projectBuilder.Append(" ").Append(Environment.NewLine); + projectBuilder.Append(" ").Append(Environment.NewLine); + foreach (var reference in assembly.AssemblyReferences.Where(i => !filteredProjects.Contains(i.name) && i.sourceFiles.Any(ShouldFileBePartOfSolution))) { + var name = GetProjectName(reference.name, reference.defines); + projectBuilder.Append(" ") + .Append(Environment.NewLine); + projectBuilder.Append(" {").Append(ProjectGuid(name)).Append("}").Append(Environment.NewLine); + projectBuilder.Append(" ").Append(name).Append("").Append(Environment.NewLine); + projectBuilder.Append(" ").Append(Environment.NewLine); + } + } + + projectBuilder.Append(ProjectFooter()); + return projectBuilder.ToString(); + } + + private static void AppendReference(string fullReference, StringBuilder projectBuilder) { + var escapedFullPath = SecurityElement.Escape(fullReference); + escapedFullPath = escapedFullPath.NormalizePath(); + projectBuilder.Append(" ").Append(Environment.NewLine); + projectBuilder.Append(" ").Append(escapedFullPath).Append("").Append(Environment.NewLine); + projectBuilder.Append(" ").Append(Environment.NewLine); + } + + private string ProjectFile(ProjectPart projectPart) { + return Path.Combine(m_SolutionDirectory, $"{GetProjectName(projectPart.Name, projectPart.Defines)}.csproj"); + } + + public string SolutionFile() { + return Path.Combine(m_SolutionDirectory, $"{m_ProjectName}.sln"); + } + + private string ProjectHeader( + ProjectPart assembly, + List responseFilesData, + Config config + ) { + var otherResponseFilesData = GetOtherArgumentsFromResponseFilesData(responseFilesData); + var arguments = new object[] { + k_ToolsVersion, + k_ProductVersion, + ProjectGuid(GetProjectName(assembly.Name, assembly.Defines)), + m_EngineAssemblyPath, + m_EditorAssemblyPath, + string.Join(";", assembly.Defines.Concat(responseFilesData.SelectMany(x => x.Defines)).Distinct().ToArray()), + MSBuildNamespaceUri, + assembly.Name, + assembly.OutputPath, + assembly.RootNamespace, + "", + GenerateLangVersion(otherResponseFilesData["langversion"], assembly), + k_BaseDirectory, + assembly.CompilerOptions.AllowUnsafeCode | responseFilesData.Any(x => x.Unsafe), + GenerateNoWarn(otherResponseFilesData["nowarn"].Distinct().ToList()), + config.excludeAllAnalyzers ? "" : GenerateAnalyserItemGroup(RetrieveRoslynAnalyzers(assembly, otherResponseFilesData)), + config.excludeAllAnalyzers ? "" : GenerateAnalyserAdditionalFiles(otherResponseFilesData["additionalfile"].SelectMany(x=>x.Split(';')).Distinct().ToArray()), + config.excludeAllAnalyzers ? "" : GenerateRoslynAnalyzerRulesetPath(assembly, otherResponseFilesData), + GenerateWarningLevel(otherResponseFilesData["warn"].Concat(otherResponseFilesData["w"]).Distinct()), + GenerateWarningAsError(otherResponseFilesData["warnaserror"], otherResponseFilesData["warnaserror-"], + otherResponseFilesData["warnaserror+"]), + GenerateDocumentationFile(otherResponseFilesData["doc"].ToArray()), + GenerateNullable(otherResponseFilesData["nullable"]) + }; + + try { + return string.Format(GetProjectHeaderTemplate(), arguments); + } catch (Exception) { + throw new NotSupportedException( + string.Format(Translations.Utility.FailedCreateCSharpProject, arguments.Length)); + } + } + + string[] RetrieveRoslynAnalyzers(ProjectPart assembly, ILookup otherResponseFilesData) { + var otherAnalyzers = otherResponseFilesData["a"] ?? Array.Empty(); + #if UNITY_2020_2_OR_NEWER + return otherResponseFilesData["analyzer"].Concat(otherAnalyzers) + .SelectMany(x=>x.Split(';')) + // #if !ROSLYN_ANALYZER_FIX + // .Concat(GetRoslynAnalyzerPaths()) + // #else + .Concat(assembly.CompilerOptions.RoslynAnalyzerDllPaths ?? Array.Empty()) + // #endif + .Select(MakeAbsolutePath) + .Distinct() + .ToArray(); + #else + return otherResponseFilesData["analyzer"].Concat(otherAnalyzers) + .SelectMany(x=>x.Split(';')) + .Distinct() + .Select(MakeAbsolutePath) + .ToArray(); + #endif + } + + private static string GenerateAnalyserItemGroup(string[] paths) { + // + // + // + // + if (!paths.Any()) + return string.Empty; + + var analyserBuilder = new StringBuilder(); + analyserBuilder.AppendLine(" "); + foreach (var path in paths) { + analyserBuilder.AppendLine($" "); + } + + analyserBuilder.AppendLine(" "); + return analyserBuilder.ToString(); + } + + private string GenerateRoslynAnalyzerRulesetPath(ProjectPart assembly, ILookup otherResponseFilesData) { +#if UNITY_2020_2_OR_NEWER + return GenerateAnalyserRuleSet(otherResponseFilesData["ruleset"].Append(assembly.CompilerOptions.RoslynAnalyzerRulesetPath) + .Where(a => !string.IsNullOrEmpty(a)).Distinct().Select(x => MakeAbsolutePath(x).NormalizePath()).ToArray()); +#else + return GenerateAnalyserRuleSet(otherResponseFilesData["ruleset"].Distinct().Select(x => MakeAbsolutePath(x).NormalizePath()).ToArray()); +#endif + } + + private static string GenerateAnalyserRuleSet(string[] paths) { + //..\path\to\myrules.ruleset + if (!paths.Any()) + return string.Empty; + + return + $"{Environment.NewLine}{string.Join(Environment.NewLine, paths.Select(a => $" {a}"))}"; + } + + private static string MakeAbsolutePath(string path) { + return Path.IsPathRooted(path) ? path : Path.GetFullPath(path); + } + + private string GenerateNullable(IEnumerable enumerable) { + var val = enumerable.FirstOrDefault(); + if (string.IsNullOrWhiteSpace(val)) + return string.Empty; + + return $"{Environment.NewLine} {val}"; + } + + private static string GenerateDocumentationFile(string[] paths) { + if (!paths.Any()) + return String.Empty; + + return $"{Environment.NewLine}{string.Join(Environment.NewLine, paths.Select(a => $" {a}"))}"; + } + + private static string GenerateWarningAsError(IEnumerable args, IEnumerable argsMinus, IEnumerable argsPlus) { + var returnValue = String.Empty; + var allWarningsAsErrors = false; + var warningIds = new List(); + + foreach (var s in args) { + if (s == "+" || s == string.Empty) allWarningsAsErrors = true; + else if (s == "-") allWarningsAsErrors = false; + else { + warningIds.Add(s); + } + } + + warningIds.AddRange(argsPlus); + + returnValue += $@" {allWarningsAsErrors}"; + if (warningIds.Any()) { + returnValue += $"{Environment.NewLine} {string.Join(";", warningIds)}"; + } + + if (argsMinus.Any()) + returnValue += $"{Environment.NewLine} {string.Join(";", argsMinus)}"; + + return $"{Environment.NewLine}{returnValue}"; + } + + private static string GenerateWarningLevel(IEnumerable warningLevel) { + var level = warningLevel.FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(level)) + return level; + + return 4.ToString(); + } + + private static string GetSolutionText() { + return string.Join(Environment.NewLine, + @"", + @"Microsoft Visual Studio Solution File, Format Version {0}", + @"# Visual Studio {1}", + @"{2}", + @"Global", + @" GlobalSection(SolutionConfigurationPlatforms) = preSolution", + @" Debug|Any CPU = Debug|Any CPU", + @" EndGlobalSection", + @" GlobalSection(ProjectConfigurationPlatforms) = postSolution", + @"{3}", + @" EndGlobalSection", + @" GlobalSection(SolutionProperties) = preSolution", + @" HideSolutionNode = FALSE", + @" EndGlobalSection", + @"EndGlobal", + @"").Replace(" ", "\t"); + } + + private static string GetProjectFooterTemplate() { + return string.Join(Environment.NewLine, + @" ", + @" ", + @" ", + @"", + @""); + } + + private static string GetProjectHeaderTemplate() { + var header = new[] { + @"", + @"", + @" ", + @" {11}", + @" <_TargetFrameworkDirectories>non_empty_path_generated_by_unity.rider.package", + @" <_FullFrameworkReferenceAssemblyPaths>non_empty_path_generated_by_unity.rider.package", + @" true{17}", + @" ", + @" ", + @" Debug", + @" AnyCPU", + @" {1}", + @" 2.0", + @" {9}", + @" {{{2}}}", + @" {{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1}};{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}", + @" Library", + @" Properties", + @" {7}", + @" v4.7.1", + @" 512", + @" {12}", + @" ", + @" ", + @" true", + @" full", + @" false", + @" {8}", + @" {5}", + @" prompt", + @" {18}", + @" {14}", + @" {13}{19}{20}{21}", + @" " + }; + + var forceExplicitReferences = new[] { + @" ", + @" true", + @" true", + @" false", + @" false", + @" false", + @" " + }; + + var footer = new[] { + @"{15}{16} ", + @"" + }; + + var pieces = header.Concat(forceExplicitReferences).Concat(footer).ToArray(); + return string.Join(Environment.NewLine, pieces); + } + + private void SyncSolution(ProjectPart[] islands) { + SyncSolutionFileIfNotChanged(SolutionFile(), SolutionText(islands)); + } + + private string SolutionText(ProjectPart[] islands) { + var fileversion = "11.00"; + var vsversion = "2010"; + + var projectEntries = GetProjectEntries(islands); + var projectConfigurations = string.Join(Environment.NewLine, + islands.Select(i => GetProjectActiveConfigurations(ProjectGuid(GetProjectName(i.Name, i.Defines)))).ToArray()); + return string.Format(GetSolutionText(), fileversion, vsversion, projectEntries, projectConfigurations); + } + + private static ILookup GetOtherArgumentsFromResponseFilesData(List responseFilesData) { + var paths = responseFilesData.SelectMany(x => { + return x.OtherArguments + .Where(a => a.StartsWith("/", StringComparison.Ordinal) || a.StartsWith("-", StringComparison.Ordinal)) + .Select(b => { + var index = b.IndexOf(":", StringComparison.Ordinal); + if (index > 0 && b.Length > index) { + var key = b.Substring(1, index - 1); + return new KeyValuePair(key.ToLowerInvariant(), b.Substring(index + 1)); + } + + const string warnaserror = "warnaserror"; + if (b.Substring(1).StartsWith(warnaserror, StringComparison.Ordinal)) { + return new KeyValuePair(warnaserror, b.Substring(warnaserror.Length + 1)); + } + + const string nullable = "nullable"; + if (b.Substring(1).StartsWith(nullable)) { + var res = b.Substring(nullable.Length + 1); + if (string.IsNullOrWhiteSpace(res) || res.Equals("+")) + res = "enable"; + else if (res.Equals("-")) + res = "disable"; + return new KeyValuePair(nullable, res); + } + + return default(KeyValuePair); + }); + }) + .Distinct() + .ToLookup(o => o.Key, pair => pair.Value); + return paths; + } + + private string GenerateLangVersion(IEnumerable langVersionList, ProjectPart assembly) { + var langVersion = langVersionList.FirstOrDefault(); + if (!string.IsNullOrWhiteSpace(langVersion)) + return langVersion; +#if UNITY_2020_2_OR_NEWER + return assembly.CompilerOptions.LanguageVersion; +#else + return k_TargetLanguageVersion; +#endif + } + + private static string GenerateAnalyserAdditionalFiles(string[] paths) { + if (!paths.Any()) + return string.Empty; + + var analyserBuilder = new StringBuilder(); + analyserBuilder.AppendLine(" "); + foreach (var path in paths) { + analyserBuilder.AppendLine($" "); + } + + analyserBuilder.AppendLine(" "); + return analyserBuilder.ToString(); + } + + public string GenerateNoWarn(List codes) { + if (m_SuppressCommonWarnings) + codes.AddRange(new[] { "0169", "0649" }); + + if (!codes.Any()) + return string.Empty; + + return string.Join(",", codes.Distinct()); + } + + private string GetProjectEntries(ProjectPart[] islands) { + var projectEntries = islands.Select(i => string.Format( + m_SolutionProjectEntryTemplate, + SolutionGuidGenerator.GuidForSolution(), + i.Name, + Path.GetFileName(ProjectFile(i)), + ProjectGuid(GetProjectName(i.Name, i.Defines)) + )); + + return string.Join(Environment.NewLine, projectEntries.ToArray()); + } + + /// + /// Generate the active configuration string for a given project guid + /// + private string GetProjectActiveConfigurations(string projectGuid) { + return string.Format( + m_SolutionProjectConfigurationTemplate, + projectGuid); + } + + private static string ProjectFooter() { + return GetProjectFooterTemplate(); + } + + + private string ProjectGuid(string name) { + return m_GUIDGenerator.ProjectGuid(m_ProjectName + name); + } + + public ProjectGenerationFlag ProjectGenerationFlag => ProjectGenerationFlag.Local | ProjectGenerationFlag.Embedded; + + public string GetAssemblyNameFromScriptPath(string path) { + return CompilationPipeline.GetAssemblyNameFromScriptPath(path); + } + + public IEnumerable GetAssemblies(Func shouldFileBePartOfSolution) { + return m_AllEditorAssemblies.Where(a => a.sourceFiles.Any(shouldFileBePartOfSolution)); + } + + private Task BuildEditorAssemblies() { + var assemblies = CompilationPipeline.GetAssemblies(AssembliesType.Editor); + return Task.Run(() => { + var result = new Assembly[assemblies.Length]; + for (var i = 0; i < assemblies.Length; i++) { + var assembly = assemblies[i]; + var outputPath = $@"Temp\Bin\Debug\{assembly.name}\"; + result[i] = new Assembly(assembly.name, outputPath, assembly.sourceFiles, + assembly.defines, + assembly.assemblyReferences, assembly.compiledAssemblyReferences, + assembly.flags, assembly.compilerOptions +#if UNITY_2020_2_OR_NEWER + , assembly.rootNamespace +#endif + ); + } + + m_AllEditorAssemblies = result; + }); + } + + public string GetProjectName(string name, string[] defines) { + if (!ProjectGenerationFlag.HasFlag(ProjectGenerationFlag.PlayerAssemblies)) + return name; + return !defines.Contains("UNITY_EDITOR") ? name + ".Player" : name; + } + + private static string ResolvePotentialParentPackageAssetPath(string assetPath) { + const string packagesPrefix = "packages/"; + if (!assetPath.StartsWith(packagesPrefix, StringComparison.OrdinalIgnoreCase)) { + return null; + } + + var followupSeparator = assetPath.IndexOf('/', packagesPrefix.Length); + if (followupSeparator == -1) { + return assetPath.ToLowerInvariant(); + } + + return assetPath.Substring(0, followupSeparator).ToLowerInvariant(); + } + + public UnityEditor.PackageManager.PackageInfo FindForAssetPath(string assetPath) { + var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(assetPath); + if (parentPackageAssetPath == null) { + return null; + } + + PackageInfo cachedPackageInfo; + if (m_PackageInfoCache.TryGetValue(parentPackageAssetPath, out cachedPackageInfo)) { + return cachedPackageInfo; + } + + return null; + } + + async Task BuildPackageInfoCache() { +#if UNITY_2019_4_OR_NEWER + m_PackageInfoCache.Clear(); + var parentAssetPaths = new HashSet(); + await Task.Run(() => { + for (var i = 0; i < m_AllAssetPaths.Length; i++) { + if (string.IsNullOrWhiteSpace(m_AllAssetPaths[i])) { + continue; + } + + var parentPackageAssetPath = ResolvePotentialParentPackageAssetPath(m_AllAssetPaths[i]); + if (parentPackageAssetPath == null) { + continue; + } + + parentAssetPaths.Add(parentPackageAssetPath); + } + }); + foreach (var parentAssetPath in parentAssetPaths) { + var result = UnityEditor.PackageManager.PackageInfo.FindForAssetPath(parentAssetPath); + m_PackageInfoCache.Add(parentAssetPath, result); + } +#else + //keep compiler happy + await Task.CompletedTask; +#endif + } + + async Task BuildPostProcessors() { +#if UNITY_2019_1_OR_NEWER + var types = TypeCache.GetTypesDerivedFrom(); + m_PostProcessors = await Task.Run(() => { + var postProcessors = new List(types.Count); + foreach (var type in types) { + try { + var instance = (IHotReloadProjectGenerationPostProcessor)Activator.CreateInstance(type); + postProcessors.Add(instance); + } catch (MissingMethodException) { + Log.Warning(Translations.Errors.WarningPostProcessorNoDefaultConstructor, type.FullName); + } catch (TargetInvocationException ex) { + Log.Warning(Translations.Errors.WarningPostProcessorConstructorException, type.FullName, ex.InnerException); + } catch (Exception ex) { + Log.Warning(Translations.Errors.WarningPostProcessorUnknownException, type.FullName, ex); + } + } + + postProcessors.Sort((x, y) => x.CallbackOrder.CompareTo(y.CallbackOrder)); + return postProcessors.ToArray(); + }); + foreach (var postProcessor in m_PostProcessors) { + postProcessor.InitializeOnMainThread(); + } +#else + m_PostProcessors = new IHotReloadProjectGenerationPostProcessor[0]; + //keep compiler happy + await Task.CompletedTask; +#endif + } + + public bool IsInternalizedPackagePath(string path) { + if (string.IsNullOrWhiteSpace(path)) { + return false; + } + + var packageInfo = FindForAssetPath(path); + if (packageInfo == null) { + return false; + } + + var packageSource = packageInfo.source; + switch (packageSource) { + case PackageSource.Embedded: + case PackageSource.Local: + return false; + default: + return true; + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs.meta new file mode 100644 index 0000000..463f3ab --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c7b221ce3667aeb4ea915b8e2eca7213 +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/ProjectGeneration/ProjectGeneration.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs new file mode 100644 index 0000000..ac6781f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs @@ -0,0 +1,18 @@ +using System; + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration +{ + [Flags] + enum ProjectGenerationFlag + { + None = 0, + Embedded = 1, + Local = 2, + Registry = 4, + Git = 8, + BuiltIn = 16, + Unknown = 32, + PlayerAssemblies = 64, + LocalTarBall = 128, + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs.meta new file mode 100644 index 0000000..5bb1bf9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ea506e49c851dbd4fa394c3ed513970c +timeCreated: 1580820569 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs new file mode 100644 index 0000000..3c2e723 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Linq; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor.Compilation; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration +{ + internal class ProjectPart + { + public string Name { get; } + public string OutputPath { get; } + public Assembly Assembly { get; } + public string AssetsProjectPart { get; } + public string[] SourceFiles { get; } + public string RootNamespace { get; } + public Assembly[] AssemblyReferences { get; } + public string[] CompiledAssemblyReferences { get; } + public string[] Defines { get; } + public ScriptCompilerOptions CompilerOptions { get; } + + public ProjectPart(string name, Assembly assembly, string assetsProjectPart, string fallbackRootNamespace, HashSet additionalSourceFiles) + { + Name = name; + Assembly = assembly; + AssetsProjectPart = assetsProjectPart; + OutputPath = assembly != null ? assembly.outputPath : "Temp/Bin/Debug"; + SourceFiles = assembly != null ? assembly.sourceFiles.Union(additionalSourceFiles).ToArray() : new string[0]; +#if UNITY_2020_2_OR_NEWER + RootNamespace = assembly != null ? assembly.rootNamespace : string.Empty; +#else + RootNamespace = fallbackRootNamespace; +#endif + AssemblyReferences = assembly != null ? assembly.assemblyReferences : new Assembly[0]; + CompiledAssemblyReferences = assembly!=null? assembly.compiledAssemblyReferences:new string[0]; + Defines = assembly != null ? assembly.defines : new string[0]; + CompilerOptions = assembly != null ? assembly.compilerOptions : new ScriptCompilerOptions(); + } + + public IEnumerable ParseResponseFileData(string projectDirectory) + { +#if UNITY_2019_1_OR_NEWER + if (Assembly == null) + return new ResponseFileData[0]; + + var systemReferenceDirectories = + CompilationPipeline.GetSystemAssemblyDirectories(Assembly.compilerOptions.ApiCompatibilityLevel); + + var responseFilesData = Assembly.compilerOptions.ResponseFiles.ToDictionary( + x => x, x => CompilationPipeline.ParseResponseFile( + x, + projectDirectory, + systemReferenceDirectories + )); + + var responseFilesWithErrors = responseFilesData.Where(x => x.Value.Errors.Any()) + .ToDictionary(x => x.Key, x => x.Value); + + if (responseFilesWithErrors.Any()) + { + foreach (var error in responseFilesWithErrors) + foreach (var valueError in error.Value.Errors) + { + Log.Error(Translations.Errors.ErrorParseError, error.Key, valueError); + } + } + + return responseFilesData.Select(x => x.Value); +#else + return new ResponseFileData[0]; +#endif + } + } + +} +#if !UNITY_2019_1_OR_NEWER +namespace UnityEditor.Compilation { + internal class ResponseFileData { + public string[] Defines; + public string[] FullPathReferences; + public string[] Errors; + public string[] OtherArguments; + public bool Unsafe; + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs.meta new file mode 100644 index 0000000..fcc626d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3067d709488a2374aba4a7b421588fe9 +timeCreated: 1604050230 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs new file mode 100644 index 0000000..1417531 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs @@ -0,0 +1,29 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace SingularityGroup.HotReload.Editor.ProjectGeneration +{ + internal static class SolutionGuidGenerator + { + public static string GuidForProject(string projectName) + { + return ComputeGuidHashFor(projectName + "salt"); + } + + public static string GuidForSolution() + { + // GUID for a C# class library: http://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs + return "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"; + } + + static string ComputeGuidHashFor(string input) + { + using (var md5 = MD5.Create()) + { + var hash = md5.ComputeHash(Encoding.Default.GetBytes(input)); + return new Guid(hash).ToString(); + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs.meta new file mode 100644 index 0000000..15b2b5a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6cced23b33c3f7541bb84997246eb6ca +timeCreated: 1580717740 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util.meta new file mode 100644 index 0000000..f4d773e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15ba2343e4c758e45a3901e7f4ad146f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs new file mode 100644 index 0000000..c58df02 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs @@ -0,0 +1,37 @@ +namespace SingularityGroup.HotReload.Editor.Util +{ + internal static class FileSystemUtil + { + + public static string FileNameWithoutExtension(string path) + { + if (string.IsNullOrEmpty(path)) + { + return ""; + } + + var indexOfDot = -1; + var indexOfSlash = 0; + for (var i = path.Length - 1; i >= 0; i--) + { + if (indexOfDot == -1 && path[i] == '.') + { + indexOfDot = i; + } + + if (path[i] == '/' || path[i] == '\\') + { + indexOfSlash = i + 1; + break; + } + } + + if (indexOfDot == -1) + { + indexOfDot = path.Length; + } + + return path.Substring(indexOfSlash, indexOfDot - indexOfSlash); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs.meta new file mode 100644 index 0000000..d26a4b6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 20b02f00e1547df45ab6ce84acc5dd0c +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/ProjectGeneration/Util/FileSystemUtil.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs new file mode 100644 index 0000000..486a32a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs @@ -0,0 +1,14 @@ +using System.IO; + +namespace SingularityGroup.HotReload.Editor.Util +{ + internal static class StringUtils + { + public static string NormalizePath(this string path) + { + return path.Replace(Path.DirectorySeparatorChar == '\\' + ? '/' + : '\\', Path.DirectorySeparatorChar); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs.meta new file mode 100644 index 0000000..395fa42 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 5418a9e29385f45488b32d5459ec2c7a +timeCreated: 1623056718 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings.meta b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings.meta new file mode 100644 index 0000000..2fd152d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc190bdaae2340c6a6d8cb811dfefe03 +timeCreated: 1674242068 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs new file mode 100644 index 0000000..8bfa052 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs @@ -0,0 +1,289 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using SingularityGroup.HotReload.Editor.Localization; +using SingularityGroup.HotReload.HarmonyLib; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + using IndicationStatus = EditorIndicationState.IndicationStatus; + + // Before Unity 2021.3, value is 0 or 1. Only value of 1 is a problem. + // From Unity 2021.3 onwards, the key is "kAutoRefreshMode". + // kAutoRefreshMode options are: + // 0: disabled + // 1: enabled + // 2: enabled outside playmode + // + // On newer Unity versions, Visual Studio is also checking the kAutoRefresh setting (but it should only check kAutoRefreshMode). + // This is making hot reload unusable and so this setting needs to also get disabled. + internal static class AutoRefreshSettingChecker { + const string autoRefreshKey = "kAutoRefresh"; + #if UNITY_2021_3_OR_NEWER + const string autoRefreshModeKey = "kAutoRefreshMode"; + #endif + + const int desiredValue = 0; + + public static bool IsUserAutoRefreshDisabled() { + int autoRefreshSetting; + #if UNITY_2021_3_OR_NEWER + if (HotReloadPrefs.DefaultAutoRefreshMode != -1) { + autoRefreshSetting = HotReloadPrefs.DefaultAutoRefreshMode; + } else { + autoRefreshSetting = EditorPrefs.GetInt(autoRefreshModeKey); + } + #else + if (HotReloadPrefs.DefaultAutoRefresh != -1) { + autoRefreshSetting = HotReloadPrefs.DefaultAutoRefresh; + } else { + autoRefreshSetting = EditorPrefs.GetInt(autoRefreshKey); + } + #endif + return autoRefreshSetting == 0; + } + + public static void Apply() { + if (HotReloadPrefs.AppliedAutoRefresh) { + return; + } + + var defaultPref = EditorPrefs.GetInt(autoRefreshKey); + HotReloadPrefs.DefaultAutoRefresh = defaultPref; + EditorPrefs.SetInt(autoRefreshKey, desiredValue); + + #if UNITY_2021_3_OR_NEWER + var defaultModePref = EditorPrefs.GetInt(autoRefreshModeKey); + HotReloadPrefs.DefaultAutoRefreshMode = defaultModePref; + EditorPrefs.SetInt(autoRefreshModeKey, desiredValue); + #endif + + HotReloadPrefs.AppliedAutoRefresh = true; + } + + public static void Check() { + if (!HotReloadPrefs.AppliedAutoRefresh) { + return; + } + + if (EditorPrefs.GetInt(autoRefreshKey) != desiredValue) { + HotReloadPrefs.DefaultAutoRefresh = -1; + } + + #if UNITY_2021_3_OR_NEWER + if (EditorPrefs.GetInt(autoRefreshModeKey) != desiredValue) { + HotReloadPrefs.DefaultAutoRefreshMode = -1; + } + #endif + } + + public static void Reset() { + if (!HotReloadPrefs.AppliedAutoRefresh) { + return; + } + + if (EditorPrefs.GetInt(autoRefreshKey) == desiredValue + && HotReloadPrefs.DefaultAutoRefresh != -1 + ) { + EditorPrefs.SetInt(autoRefreshKey, HotReloadPrefs.DefaultAutoRefresh); + } + HotReloadPrefs.DefaultAutoRefresh = -1; + + #if UNITY_2021_3_OR_NEWER + if (EditorPrefs.GetInt(autoRefreshModeKey) == desiredValue + && HotReloadPrefs.DefaultAutoRefreshMode != -1 + ) { + EditorPrefs.SetInt(autoRefreshModeKey, HotReloadPrefs.DefaultAutoRefreshMode); + } + HotReloadPrefs.DefaultAutoRefreshMode = -1; + #endif + + HotReloadPrefs.AppliedAutoRefresh = false; + } + } + + internal static class ScriptCompilationSettingChecker { + const string scriptCompilationKey = "ScriptCompilationDuringPlay"; + + const int recompileAndContinuePlaying = 0; + static int? recompileAfterFinishedPlaying = (int?)typeof(EditorWindow).Assembly.GetType("UnityEditor.ScriptChangesDuringPlayOptions")? + .GetField("RecompileAfterFinishedPlaying", BindingFlags.Static | BindingFlags.Public)? + .GetValue(null); + + public static void Apply() { + if (HotReloadPrefs.AppliedScriptCompilation) { + return; + } + + var defaultPref = EditorPrefs.GetInt(scriptCompilationKey); + HotReloadPrefs.DefaultScriptCompilation = defaultPref; + EditorPrefs.SetInt(scriptCompilationKey, GetRecommendedAutoScriptCompilationKey()); + + HotReloadPrefs.AppliedScriptCompilation = true; + } + + public static void Check() { + if (!HotReloadPrefs.AppliedScriptCompilation) { + return; + } + if (EditorPrefs.GetInt(scriptCompilationKey) != GetRecommendedAutoScriptCompilationKey()) { + HotReloadPrefs.DefaultScriptCompilation = -1; + } + } + + public static void Reset() { + if (!HotReloadPrefs.AppliedScriptCompilation) { + return; + } + if (EditorPrefs.GetInt(scriptCompilationKey) == GetRecommendedAutoScriptCompilationKey() + && HotReloadPrefs.DefaultScriptCompilation != -1 + ) { + EditorPrefs.SetInt(scriptCompilationKey, HotReloadPrefs.DefaultScriptCompilation); + } + HotReloadPrefs.DefaultScriptCompilation = -1; + + HotReloadPrefs.AppliedScriptCompilation = false; + } + + static int GetRecommendedAutoScriptCompilationKey() { + // In some projects due to an unknown reason both "RecompileAndContinuePlaying" and "StopPlayingAndRecompile" cause issues + // We were unable to identify the cause and therefore we always try to default to "RecompileAfterFinishedPlaying" + // The exact issue users are experiencing is that domain reload happens shortly after entering play mode causing nullrefs + return recompileAfterFinishedPlaying ?? recompileAndContinuePlaying; + } + } + + internal static class PlaymodeTintSettingChecker { + private static readonly Color unsupportedPlaymodeColor = new Color(1f, 0.8f, 0f, 1f); + private static readonly Color compilePlaymodeErrorColor = new Color(1f, 0.7f, 0.7f, 1f); + + public static void Apply() { + if (HotReloadPrefs.AppliedEditorTint != null || !UnitySettingsHelper.I.playmodeTintSupported) { + return; + } + var defaultPref = HotReloadPrefs.DefaultEditorTint ?? UnitySettingsHelper.I.GetCurrentPlaymodeColor(); + if (defaultPref == null) { + return; + } + HotReloadPrefs.DefaultEditorTint = defaultPref.Value; + var currentPlaymodeTint = GetModifiedPlaymodeTint() ?? defaultPref.Value; + SetPlaymodeTint(currentPlaymodeTint); + } + + public static void Check() { + if (HotReloadPrefs.AppliedEditorTint == null || !UnitySettingsHelper.I.playmodeTintSupported) { + return; + } + // if user modifies the settings manually, prevent the setting to be changed + if (HotReloadPrefs.DefaultEditorTint == null || UnitySettingsHelper.I.GetCurrentPlaymodeColor() != HotReloadPrefs.AppliedEditorTint) { + HotReloadPrefs.DefaultEditorTint = null; + return; + } + var color = GetModifiedPlaymodeTint(); + if (color != null && color != HotReloadPrefs.AppliedEditorTint) { + SetPlaymodeTint(color.Value); + } + } + + + public static void Reset() { + if (HotReloadPrefs.AppliedEditorTint == null || !UnitySettingsHelper.I.playmodeTintSupported) { + return; + } + var color = HotReloadPrefs.DefaultEditorTint; + if (color != null && UnitySettingsHelper.I.GetCurrentPlaymodeColor() == HotReloadPrefs.AppliedEditorTint) { + SetPlaymodeTint(color.Value); + } + + HotReloadPrefs.DefaultEditorTint = null; + HotReloadPrefs.AppliedEditorTint = null; + } + + + private static void SetPlaymodeTint(Color color) { + UnitySettingsHelper.I.SetPlaymodeTint(color); + HotReloadPrefs.AppliedEditorTint = color; + } + + private static Color? GetModifiedPlaymodeTint() { + switch (EditorIndicationState.CurrentIndicationStatus) { + case IndicationStatus.CompileErrors: + return compilePlaymodeErrorColor; + case IndicationStatus.Unsupported: + return unsupportedPlaymodeColor; + default: + return HotReloadPrefs.DefaultEditorTint; + } + } + } + + internal static class CompileMethodDetourer { + static bool detouredMethod; + static List reverters = new List(); + + public static void Apply() { + if (detouredMethod) { + return; + } + detouredMethod = true; + + var originAssetRefresh = typeof(AssetDatabase).GetMethod(nameof(AssetDatabase.Refresh), Type.EmptyTypes); + var targetAssetRefresh = typeof(CompileMethodDetourer).GetMethod(nameof(DetouredAssetRefresh)); + + DetourMethod(originAssetRefresh, targetAssetRefresh); + + var originAssetRefreshWithParams = typeof(AssetDatabase).GetMethod(nameof(AssetDatabase.Refresh), new[] { typeof(ImportAssetOptions) }); + var targetAssetRefreshWithParams = typeof(CompileMethodDetourer).GetMethod(nameof(DetouredAssetRefresh)); + + DetourMethod(originAssetRefreshWithParams, targetAssetRefreshWithParams); + + var originCompilation = typeof(CompilationPipeline).GetMethod(nameof(CompilationPipeline.RequestScriptCompilation), Type.EmptyTypes); + var targetCompilation = typeof(CompileMethodDetourer).GetMethod(nameof(RequestScriptCompilation)); + + DetourMethod(originCompilation, targetCompilation); + } + + static void DetourMethod(MethodBase original, MethodBase replacement) { + DetourResult result; + DetourApi.DetourMethod(original, replacement, out result); + + if (!result.success) { + Debug.LogWarning(string.Format(Translations.Errors.DebugDetouringMethodFailed, original.Name, result.exception?.GetType(), result.exception)); + } else { + reverters.Add(result.patchRecord); + } + } + + public static void Reset() { + if (!detouredMethod) { + return; + } + + detouredMethod = false; + + // don't revert for now + // foreach (var reverter in reverters) { + // try { + // reverter.Dispose(); + // } catch (Exception exc) { + // Debug.LogWarning($"Reverting method detour failed. {exc.GetType()} {exc}"); + // } + // } + + reverters.Clear(); + + // hack to undo changes to Editor assemblies. + // Doing this when starting hotreload cancels the start + // Exit playmode right away to prevent delayed compiling + EditorApplication.isPlaying = false; + + EditorApplication.ExecuteMenuItem("Assets/Refresh"); + EditorUtility.RequestScriptReload(); //this will undo the modifications to the assemblies + } + + public static void DetouredAssetRefresh(ImportAssetOptions options) { } + public static void RequestScriptCompilation() { } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs.meta new file mode 100644 index 0000000..343d808 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 63765d77daad497ba3966a622a486391 +timeCreated: 1674243489 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources.meta new file mode 100644 index 0000000..f14fddd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 030ba7c6c9994974b321f9c38df270ad +timeCreated: 1673704008 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png new file mode 100644 index 0000000..a27133c Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png.meta new file mode 100644 index 0000000..31627ca --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: bef6749cf91a43b4e8368429a0487ac9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 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: Android + 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: [] + 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/Resources/Icn_Stop.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png new file mode 100644 index 0000000..429e1be Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png.meta new file mode 100644 index 0000000..66b3bfc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 052ef782f5237784eab3e4952a88afde +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 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: Android + 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: [] + 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/Resources/Icn_play.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png new file mode 100644 index 0000000..5f144aa Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png.meta new file mode 100644 index 0000000..deb6fd3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 0c501457e83420242a3ac14642818be3 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + 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/Resources/Logo_HotReload_DarkMode.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png new file mode 100644 index 0000000..bfaeb4a Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png.meta new file mode 100644 index 0000000..c4f18ea --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 4a83d4a4d357e18408c025ef0737ab39 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + 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/Resources/Logo_HotReload_LightMode.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png new file mode 100644 index 0000000..f17c65b Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png.meta new file mode 100644 index 0000000..c6a56c7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 2699d640b2d1cdd45be9a46539d5018e +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/alert_info.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png new file mode 100644 index 0000000..dc4552c Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png.meta new file mode 100644 index 0000000..4d520ff --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 3e8fc7297345815e0a9416a6b0d4cd68 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/applied_patch.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png new file mode 100644 index 0000000..a16c26e Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png.meta new file mode 100644 index 0000000..511d73c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png.meta @@ -0,0 +1,99 @@ +fileFormatVersion: 2 +guid: 8909f5f9b1bbe3a4e99335241d585b33 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + 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/Resources/blue.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png new file mode 100644 index 0000000..fc5f16c Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png.meta new file mode 100644 index 0000000..dcbc17e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/close.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 66f82957eba5f310ab1e99f70abee8bb +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/close.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png new file mode 100644 index 0000000..f5a0bd7 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png.meta new file mode 100644 index 0000000..37edcb2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/error.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 6ac08405045af608fb1dc9cc2c981d64 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/error.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png new file mode 100644 index 0000000..0cbc272 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png.meta new file mode 100644 index 0000000..c03c3ff --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/events.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: b2c1a1b6177ddd425ba5b4879dd5903a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/events.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png new file mode 100644 index 0000000..ad482a1 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png.meta new file mode 100644 index 0000000..9849ddf --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: ae8d6e8a046a7ba47937cf15819407e3 +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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 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 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/foldout_closed.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png new file mode 100644 index 0000000..928bbfa Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png.meta new file mode 100644 index 0000000..e6c01a2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 4b7efc0ab40ddb750a17f65ecdcdbe95 +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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 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 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/foldout_open.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png new file mode 100644 index 0000000..1c3184c Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png.meta new file mode 100644 index 0000000..3d66584 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/green.png.meta @@ -0,0 +1,131 @@ +fileFormatVersion: 2 +guid: 45ccf4d7106fc972cb676d15f014f50c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 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: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 109.07 + 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: 1 + swizzle: 50462976 + cookieLightType: 1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/green.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png new file mode 100644 index 0000000..786e530 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png.meta new file mode 100644 index 0000000..640dc82 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png.meta @@ -0,0 +1,131 @@ +fileFormatVersion: 2 +guid: c381173cda2dd9304b52e11e1a7537bd +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/grey.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png new file mode 100644 index 0000000..7f99562 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png.meta new file mode 100644 index 0000000..4eb00e1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 608000d4abfb24f13a03e1f626655875 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + 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/Resources/icon_check_circle.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png new file mode 100644 index 0000000..53c2200 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png.meta new file mode 100644 index 0000000..2c69dad --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png.meta @@ -0,0 +1,154 @@ +fileFormatVersion: 2 +guid: e74855f29a0ba40b18b2216d398ba359 +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: 2 + 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/Resources/icon_circle_black.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png new file mode 100644 index 0000000..fac1070 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png.meta new file mode 100644 index 0000000..45095bf --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png.meta @@ -0,0 +1,154 @@ +fileFormatVersion: 2 +guid: 809ef8ee76ad04e198cc8ba61c994f83 +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: 2 + 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/Resources/icon_circle_gray.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png new file mode 100644 index 0000000..317ba37 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png.meta new file mode 100644 index 0000000..5e9ecd8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png.meta @@ -0,0 +1,131 @@ +fileFormatVersion: 2 +guid: 725116e63c640c8408ca1c393191bc99 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 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: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 109.07 + 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: 1 + swizzle: 50462976 + cookieLightType: 1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/icon_loading_star_light_mode_96.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png new file mode 100644 index 0000000..9c4d478 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png.meta new file mode 100644 index 0000000..a73ec22 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 0f8d84c5136cc4c119f14ee9797fb05f +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + 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 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + 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/Resources/icon_warning_circle.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png new file mode 100644 index 0000000..5a8ed9b Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png.meta new file mode 100644 index 0000000..25c847f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: afb7219da2e3aa342864d839baf48862 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/infos.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png new file mode 100644 index 0000000..7707ead Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png.meta new file mode 100644 index 0000000..37ed332 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 0779c2964d0997956acf3c29e4e38a5c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/logo.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png new file mode 100644 index 0000000..44c2c9b Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png.meta new file mode 100644 index 0000000..4ed624d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/red.png.meta @@ -0,0 +1,131 @@ +fileFormatVersion: 2 +guid: b0408809d04ad6d719d1e4d66c181cc2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/red.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png new file mode 100644 index 0000000..5c43f67 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png.meta new file mode 100644 index 0000000..fee0a78 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 163438a66048768a3b73c44f40b234aa +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/red_dot.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png new file mode 100644 index 0000000..d5c5789 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png.meta new file mode 100644 index 0000000..e76d773 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 75c3ce1c05b6563dfbd2ac68fbb63c79 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/refresh.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png new file mode 100644 index 0000000..9754cfb Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png.meta new file mode 100644 index 0000000..be3a2b4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 77c9c0af0f612d0a0a94cf95e2235730 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/report_bug.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png new file mode 100644 index 0000000..50c857a Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png.meta new file mode 100644 index 0000000..6e56df1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/undetected.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 8583124d8e2f5ab40a98e2a5742c75bd +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + 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: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + 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 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 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: Android + 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: [] + 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/Resources/undetected.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png new file mode 100644 index 0000000..b1ad63e Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png.meta new file mode 100644 index 0000000..d96f888 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png.meta @@ -0,0 +1,134 @@ +fileFormatVersion: 2 +guid: 277421eb559ad2d3eba0c5e401f98777 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 1 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 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 + ignorePlatformSupport: 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 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/warning.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png b/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png new file mode 100644 index 0000000..4d81c86 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png.meta new file mode 100644 index 0000000..a6ef940 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png.meta @@ -0,0 +1,131 @@ +fileFormatVersion: 2 +guid: 90079bcff5ee4ece784cb51fb94a4b1d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + 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 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + 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: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 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/Resources/yellow.png + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs new file mode 100644 index 0000000..f5fed88 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; +using SingularityGroup.HotReload.Editor.Cli; + +namespace SingularityGroup.HotReload.Editor { + public class ServerHealthCheck : IServerHealthCheckInternal { + private static readonly TimeSpan heartBeatTimeout = TimeSpan.FromMilliseconds(5000); + internal static readonly IServerHealthCheckInternal instance = new ServerHealthCheck(); + + public static IServerHealthCheck I => instance; + public static TimeSpan HeartBeatTimeout => heartBeatTimeout; + + ServerHealthCheck() { } + + /// + /// Whether or not the server is running and responsive + /// + public bool IsServerHealthy { get; private set; } + + void IServerHealthCheckInternal.CheckHealth() { + var fi = new FileInfo(Path.Combine(CliUtils.GetCliTempDir(), "health")); + IsServerHealthy = fi.Exists && DateTime.UtcNow - fi.LastWriteTimeUtc < heartBeatTimeout; + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs.meta new file mode 100644 index 0000000..7a6572c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 45b5951115b94f6ab79756f71777a915 +timeCreated: 1673812440 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef new file mode 100644 index 0000000..a47a0d4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef @@ -0,0 +1,32 @@ +{ + "name": "SingularityGroup.HotReload.Editor", + "rootNamespace": "SingularityGroup.HotReload", + "references": [ + "SingularityGroup.HotReload.Runtime.Public", + "SingularityGroup.HotReload.Runtime", + "UnityEditor.UI", + "UnityEngine.UI", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner", + "VInspector" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "SingularityGroup.HotReload.RuntimeDependencies.dll", + "SingularityGroup.HotReload.RuntimeDependencies2019.dll", + "SingularityGroup.HotReload.RuntimeDependencies2020.dll", + "SingularityGroup.HotReload.RuntimeDependencies2022.dll", + "SingularityGroup.HotReload.EditorDependencies.dll", + "Sirenix.OdinInspector.Editor.dll", + "Sirenix.Utilities.dll", + "Sirenix.OdinInspector.Attributes.dll" + ], + "autoReferenced": false, + "defineConstraints": [] +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef.meta b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef.meta new file mode 100644 index 0000000..8e46402 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 43aab4a50a5839e44af6c24ac5006688 +AssemblyDefinitionImporter: + externalObjects: {} + 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/SingularityGroup.HotReload.Editor.asmdef + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll new file mode 100644 index 0000000..6514c03 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll.meta b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll.meta new file mode 100644 index 0000000..2d0e40d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll.meta @@ -0,0 +1,40 @@ +fileFormatVersion: 2 +guid: 2286e58e1d144b84c925391e896974b2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + 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/SingularityGroup.HotReload.EditorDependencies.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning.meta b/Packages/com.singularitygroup.hotreload/Editor/Versioning.meta new file mode 100644 index 0000000..0477de0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5726f82867506c8408235866081bec1d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs b/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs new file mode 100644 index 0000000..fa107ec --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs @@ -0,0 +1,295 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Editor.Localization; +using SingularityGroup.HotReload.Localization; +using SingularityGroup.HotReload.Editor.Semver; +using SingularityGroup.HotReload.Newtonsoft.Json; +using SingularityGroup.HotReload.Newtonsoft.Json.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; +using Translations = SingularityGroup.HotReload.Editor.Localization.Translations; + +namespace SingularityGroup.HotReload.Editor { + internal class PackageUpdateChecker { + static readonly string persistedFile = PackageConst.LibraryCachePath + "/updateChecker.json"; + readonly JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(); + SemVersion newVersionDetected; + bool started; + bool warnedVersionCheckFailed; + + private static TimeSpan RetryInterval => TimeSpan.FromSeconds(30); + private static TimeSpan CheckInterval => TimeSpan.FromHours(1); + + private static readonly HttpClient client = HttpClientUtils.CreateHttpClient(); + + private static string _lastRemotePackageVersion; + + public static string lastRemotePackageVersion => _lastRemotePackageVersion; + + public async void StartCheckingForNewVersion() { + if(started) { + return; + } + started = true; + + for (;;) { + try { + await PerformVersionCheck(); + if(newVersionDetected != null) { + break; + } + } catch(Exception ex) { + Log.Warning(Translations.Errors.WarningVersionCheckException, ex); + } + await Task.Delay(RetryInterval); + } + } + + public bool TryGetNewVersion(out SemVersion version) { + var currentVersion = SemVersion.Parse(PackageConst.Version, strict: true); + return !ReferenceEquals(version = newVersionDetected, null) && newVersionDetected > currentVersion; + } + + async Task PerformVersionCheck() { + var state = await LoadPersistedState(); + var currentVersion = SemVersion.Parse(PackageConst.Version, strict: true); + if(state != null) { + _lastRemotePackageVersion = state.lastRemotePackageVersion; + var newVersion = SemVersion.Parse(state.lastRemotePackageVersion); + if(newVersion > currentVersion) { + newVersionDetected = newVersion; + return; + } + if(DateTime.UtcNow - state.lastVersionCheck < CheckInterval) { + return; + } + } + + var response = await GetLatestPackageVersion(); + if(response.err != null) { + if(response.statusCode == 0 || response.statusCode == 404) { + // probably no internet, fail silently and retry + } else if (!warnedVersionCheckFailed) { + Log.Warning(Translations.Errors.WarningVersionCheckFailed, response.err); + warnedVersionCheckFailed = true; + } + } else { + var newVersion = response.data; + if (response.data > currentVersion) { + newVersionDetected = newVersion; + } + await Task.Run(() => PersistState(response.data)); + } + } + + void PersistState(SemVersion newVersion) { + // ReSharper disable once AssignNullToNotNullAttribute + var fi = new FileInfo(persistedFile); + fi.Directory.Create(); + using (var streamWriter = new StreamWriter(fi.OpenWrite())) + using (var writer = new JsonTextWriter(streamWriter)) { + jsonSerializer.Serialize(writer, new State { + lastVersionCheck = DateTime.UtcNow, + lastRemotePackageVersion = newVersion.ToString() + }); + } + } + + Task LoadPersistedState() { + return Task.Run(() => { + var fi = new FileInfo(persistedFile); + if(!fi.Exists) { + return null; + } + + using(var streamReader = fi.OpenText()) + using(var reader = new JsonTextReader(streamReader)) { + return jsonSerializer.Deserialize(reader); + } + }); + } + + + + static async Task> GetLatestPackageVersion() { + string versionUrl; + + if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) { + versionUrl = "https://d2tc55zjhw51ly.cloudfront.net/releases/latest/asset-store-version-zh.json"; + } else if (PackageConst.IsAssetStoreBuild) { + // version updates are synced with asset store + versionUrl = "https://d2tc55zjhw51ly.cloudfront.net/releases/latest/asset-store-version.json"; + } else { + versionUrl = "https://gitlab.hotreload.net/root/hot-reload-releases/-/raw/production/package.json"; + } + try { + using(var resp = await client.GetAsync(versionUrl).ConfigureAwait(false)) { + if(resp.StatusCode != HttpStatusCode.OK) { + return Response.FromError(string.Format(Translations.Errors.ErrorRequestFailedStatusCode, resp.StatusCode, resp.ReasonPhrase)); + } + + var json = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + var o = await JObject.LoadAsync(new JsonTextReader(new StringReader(json))).ConfigureAwait(false); + SemVersion newVersion; + JToken value; + if (!o.TryGetValue("version", out value)) { + return Response.FromError(Translations.Errors.ErrorInvalidPackageJson); + } else if(!SemVersion.TryParse(value.Value(), out newVersion, strict: true)) { + return Response.FromError(string.Format(Translations.Errors.ErrorInvalidVersionInPackageJson, value.Value())); + } else { + return Response.FromResult(newVersion); + } + } + } catch(Exception ex) { + return Response.FromError($"{ex.GetType().Name} {ex.Message}"); + } + } + + public async Task UpdatePackageAsync(SemVersion newVersion) { + //Package can be updated by updating the git url via the package manager + if(EditorUtility.DisplayDialog(string.Format(Translations.Dialogs.DialogTitleUpdateFormat, newVersion), string.Format(Translations.Dialogs.DialogMessageUpdateFormat, newVersion), Translations.Dialogs.DialogButtonUpdate, Translations.Common.ButtonCancel)) { + var resp = await GetLatestPackageVersion(); + if(resp.err == null && resp.data > newVersion) { + newVersion = resp.data; + } + + if(await IsUsingGitRepo()) { + var err = UpdateGitUrlInManifest(newVersion); + if(err != null) { + Log.Warning(Translations.Errors.WarningUpdateIssueFailed, err); + } else { + //Delete state to force another version check after the package is installed + File.Delete(persistedFile); + #if UNITY_2020_3_OR_NEWER + UnityEditor.PackageManager.Client.Resolve(); + #else + CompileMethodDetourer.Reset(); + AssetDatabase.Refresh(); + #endif + } + } else { + var err = await UpdateUtility.Update(newVersion.ToString(), null, CancellationToken.None); + if(err != null) { + Log.Warning(Translations.Errors.WarningUpdatePackageFailed, err); + } else { + CompileMethodDetourer.Reset(); + AssetDatabase.Refresh(); + } + } + + //open changelog + HotReloadPrefs.ShowChangeLog = true; + HotReloadWindow.Current.SelectTab(typeof(HotReloadAboutTab)); + } + } + + string UpdateGitUrlInManifest(SemVersion newVersion) { + const string repoUrl = "git+https://gitlab.hotreload.net/root/hot-reload-releases.git"; + const string manifestJsonPath = "Packages/manifest.json"; + var repoUrlToNewVersion = $"{repoUrl}#{newVersion}"; + if(!File.Exists(manifestJsonPath)) { + return Translations.Errors.ErrorUnableToFindManifestJson; + } + + var root = JObject.Load(new JsonTextReader(new StringReader(File.ReadAllText(manifestJsonPath)))); + JObject deps; + var err = TryGetManfestDeps(root, out deps); + if(err != null) { + return err; + } + deps[PackageConst.PackageName] = repoUrlToNewVersion; + root["dependencies"] = deps; + File.WriteAllText(manifestJsonPath, root.ToString(Formatting.Indented)); + return null; + } + + static string TryGetManfestDeps(JObject root, out JObject deps) { + JToken value; + if(!root.TryGetValue("dependencies", out value)) { + deps = null; + return Translations.Errors.ErrorNoDependenciesInManifest; + } + deps = value.Value(); + if(deps == null) { + return Translations.Errors.ErrorDependenciesNullInManifest; + } + return null; + } + + static async Task IsUsingGitRepo() { + var respose = await Task.Run(() => IsUsingGitRepoThreaded(PackageConst.PackageName)); + if(respose.err != null) { + Log.Warning(Translations.Errors.WarningUnableToFindPackage, respose.err); + return false; + } else { + return respose.data; + } + } + + static Response IsUsingGitRepoThreaded(string packageId) { + var fi = new FileInfo("Packages/manifest.json"); + if(!fi.Exists) { + return Translations.Errors.ErrorUnableToFindManifestJson; + } + + using(var reader = fi.OpenText()) { + var root = JObject.Load(new JsonTextReader(reader)); + JObject deps; + var err = TryGetManfestDeps(root, out deps); + if(err != null) { + return Translations.Errors.ErrorNoDependenciesSpecified; + } + JToken value; + if(!deps.TryGetValue(packageId, out value)) { + //Likely a local package directly in the packages folder of the unity project + //or the package got moved into the Assets folder + return Response.FromResult(false); + } + var pathToPackage = value.Value(); + if(pathToPackage.StartsWith("git+", StringComparison.Ordinal)) { + return Response.FromResult(true); + } + if(pathToPackage.StartsWith("https://", StringComparison.Ordinal)) { + return Response.FromResult(true); + } + return Response.FromResult(false); + } + } + + class Response { + public readonly T data; + public readonly string err; + public readonly long statusCode; + public Response(T data, string err, long statusCode) { + this.data = data; + this.err = err; + this.statusCode = statusCode; + } + + public static implicit operator Response( string err) { + return Response.FromError(err); + } + } + + static class Response { + public static Response FromError(string error) { + return new Response(default(T), error, -1); + } + public static Response FromResult(T result) { + return new Response(result, null, 200); + } + } + + class State { + public DateTime lastVersionCheck; + public string lastRemotePackageVersion; + } + } + + +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs.meta new file mode 100644 index 0000000..9784621 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0cecc9e1c2704904691175cdd2f450e6 +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/Versioning/PackageUpdateChecker.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs b/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs new file mode 100644 index 0000000..65dd704 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs @@ -0,0 +1,537 @@ +using System; +#if !NETSTANDARD +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif +using System.Text.RegularExpressions; +using SingularityGroup.HotReload.Editor.Localization; + +namespace SingularityGroup.HotReload.Editor.Semver +{ + /// + /// A semantic version implementation. + /// Conforms to v2.0.0 of http://semver.org/ + /// +#if NETSTANDARD + sealed class SemVersion : IComparable, IComparable +#else + [Serializable] + sealed class SemVersion : IComparable, IComparable, ISerializable +#endif + { + public static SemVersion None = new SemVersion(0, 0, 0); + public static string NoneString = new SemVersion(0, 0, 0).ToString(); + + static Regex parseEx = + new Regex(@"^(?\d+)" + + @"(\.(?\d+))?" + + @"(\.(?\d+))?" + + @"(\-(?
[0-9A-Za-z\-\.]+))?" +
+                @"(\+(?[0-9A-Za-z\-\.]+))?$",
+#if NETSTANDARD
+                RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture);
+#else
+                RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
+#endif
+
+#if !NETSTANDARD
+        /// 
+        /// Initializes a new instance of the  class.
+        /// 
+        /// 
+        /// 
+        /// 
+        private SemVersion(SerializationInfo info, StreamingContext context)
+        {
+            if (info == null) throw new ArgumentNullException("info");
+            var semVersion = Parse(info.GetString("SemVersion"));
+            Major = semVersion.Major;
+            Minor = semVersion.Minor;
+            Patch = semVersion.Patch;
+            Prerelease = semVersion.Prerelease;
+            Build = semVersion.Build;
+        }
+#endif
+
+        /// 
+        /// Initializes a new instance of the  class.
+        /// 
+        /// The major version.
+        /// The minor version.
+        /// The patch version.
+        /// The prerelease version (eg. "alpha").
+        /// The build eg ("nightly.232").
+        public SemVersion(int major, int minor = 0, int patch = 0, string prerelease = "", string build = "")
+        {
+            this.Major = major;
+            this.Minor = minor;
+            this.Patch = patch;
+
+            this.Prerelease = prerelease ?? "";
+            this.Build = build ?? "";
+        }
+
+        /// 
+        /// Initializes a new instance of the  class.
+        /// 
+        /// The  that is used to initialize 
+        /// the Major, Minor, Patch and Build properties.
+        public SemVersion(Version version)
+        {
+            if (version == null)
+                throw new ArgumentNullException("version");
+
+            this.Major = version.Major;
+            this.Minor = version.Minor;
+
+            if (version.Revision >= 0)
+            {
+                this.Patch = version.Revision;
+            }
+
+            this.Prerelease = String.Empty;
+
+            if (version.Build > 0)
+            {
+                this.Build = version.Build.ToString();
+            }
+            else
+            {
+                this.Build = String.Empty;
+            }
+        }
+
+        /// 
+        /// Parses the specified string to a semantic version.
+        /// 
+        /// The version string.
+        /// If set to true minor and patch version are required, else they default to 0.
+        /// The SemVersion object.
+        /// When a invalid version string is passed.
+        public static SemVersion Parse(string version, bool strict = false)
+        {
+            var match = parseEx.Match(version);
+            if (!match.Success)
+                throw new ArgumentException("Invalid version.", "version");
+
+#if NETSTANDARD
+            var major = int.Parse(match.Groups["major"].Value);
+#else
+            var major = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
+#endif
+
+            var minorMatch = match.Groups["minor"];
+            int minor = 0;
+            if (minorMatch.Success) 
+            {
+#if NETSTANDARD
+                minor = int.Parse(minorMatch.Value);
+#else
+                minor = int.Parse(minorMatch.Value, CultureInfo.InvariantCulture);
+#endif
+            }
+            else if (strict)
+            {
+                throw new InvalidOperationException(Translations.Utility.InvalidVersionNoMinor);
+            }
+
+            var patchMatch = match.Groups["patch"];
+            int patch = 0;
+            if (patchMatch.Success)
+            {
+#if NETSTANDARD
+                patch = int.Parse(patchMatch.Value);
+#else
+                patch = int.Parse(patchMatch.Value, CultureInfo.InvariantCulture);
+#endif
+            }
+            else if (strict) 
+            {
+                throw new InvalidOperationException(Translations.Utility.InvalidVersionNoPatch);
+            }
+
+            var prerelease = match.Groups["pre"].Value;
+            var build = match.Groups["build"].Value;
+
+            return new SemVersion(major, minor, patch, prerelease, build);
+        }
+
+        /// 
+        /// Parses the specified string to a semantic version.
+        /// 
+        /// The version string.
+        /// When the method returns, contains a SemVersion instance equivalent 
+        /// to the version string passed in, if the version string was valid, or null if the 
+        /// version string was not valid.
+        /// If set to true minor and patch version are required, else they default to 0.
+        /// False when a invalid version string is passed, otherwise true.
+        public static bool TryParse(string version, out SemVersion semver, bool strict = false)
+        {
+            try
+            {
+                semver = Parse(version, strict);
+                return true;
+            }
+            catch (Exception)
+            {
+                semver = null;
+                return false;
+            }
+        }
+
+        /// 
+        /// Tests the specified versions for equality.
+        /// 
+        /// The first version.
+        /// The second version.
+        /// If versionA is equal to versionB true, else false.
+        public static bool Equals(SemVersion versionA, SemVersion versionB)
+        {
+            if (ReferenceEquals(versionA, null))
+                return ReferenceEquals(versionB, null);
+            return versionA.Equals(versionB);
+        }
+
+        /// 
+        /// Compares the specified versions.
+        /// 
+        /// The version to compare to.
+        /// The version to compare against.
+        /// If versionA < versionB < 0, if versionA > versionB > 0,
+        /// if versionA is equal to versionB 0.
+        public static int Compare(SemVersion versionA, SemVersion versionB)
+        {
+            if (ReferenceEquals(versionA, null))
+                return ReferenceEquals(versionB, null) ? 0 : -1;
+            return versionA.CompareTo(versionB);
+        }
+
+        /// 
+        /// Make a copy of the current instance with optional altered fields. 
+        /// 
+        /// The major version.
+        /// The minor version.
+        /// The patch version.
+        /// The prerelease text.
+        /// The build text.
+        /// The new version object.
+        public SemVersion Change(int? major = null, int? minor = null, int? patch = null,
+            string prerelease = null, string build = null)
+        {
+            return new SemVersion(
+                major ?? this.Major,
+                minor ?? this.Minor,
+                patch ?? this.Patch,
+                prerelease ?? this.Prerelease,
+                build ?? this.Build);
+        }
+
+        /// 
+        /// Gets the major version.
+        /// 
+        /// 
+        /// The major version.
+        /// 
+        public int Major { get; private set; }
+
+        /// 
+        /// Gets the minor version.
+        /// 
+        /// 
+        /// The minor version.
+        /// 
+        public int Minor { get; private set; }
+
+        /// 
+        /// Gets the patch version.
+        /// 
+        /// 
+        /// The patch version.
+        /// 
+        public int Patch { get; private set; }
+
+        /// 
+        /// Gets the pre-release version.
+        /// 
+        /// 
+        /// The pre-release version.
+        /// 
+        public string Prerelease { get; private set; }
+
+        /// 
+        /// Gets the build version.
+        /// 
+        /// 
+        /// The build version.
+        /// 
+        public string Build { get; private set; }
+
+        /// 
+        /// Returns a  that represents this instance.
+        /// 
+        /// 
+        /// A  that represents this instance.
+        /// 
+        public override string ToString()
+        {
+            var version = "" + Major + "." + Minor + "." + Patch;
+            if (!String.IsNullOrEmpty(Prerelease))
+                version += "-" + Prerelease;
+            if (!String.IsNullOrEmpty(Build))
+                version += "+" + Build;
+            return version;
+        }
+
+        /// 
+        /// Compares the current instance with another object of the same type and returns an integer that indicates 
+        /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the 
+        /// other object.
+        /// 
+        /// An object to compare with this instance.
+        /// 
+        /// A value that indicates the relative order of the objects being compared. 
+        /// The return value has these meanings: Value Meaning Less than zero 
+        ///  This instance precedes  in the sort order. 
+        ///  Zero This instance occurs in the same position in the sort order as . i
+        ///  Greater than zero This instance follows  in the sort order.
+        /// 
+        public int CompareTo(object obj)
+        {
+            return CompareTo((SemVersion)obj);
+        }
+
+        /// 
+        /// Compares the current instance with another object of the same type and returns an integer that indicates 
+        /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the 
+        /// other object.
+        /// 
+        /// An object to compare with this instance.
+        /// 
+        /// A value that indicates the relative order of the objects being compared. 
+        /// The return value has these meanings: Value Meaning Less than zero 
+        ///  This instance precedes  in the sort order. 
+        ///  Zero This instance occurs in the same position in the sort order as . i
+        ///  Greater than zero This instance follows  in the sort order.
+        /// 
+        public int CompareTo(SemVersion other)
+        {
+            if (ReferenceEquals(other, null))
+                return 1;
+
+            var r = this.CompareByPrecedence(other);
+            if (r != 0)
+                return r;
+
+            r = CompareComponent(this.Build, other.Build);
+            return r;
+        }
+
+        /// 
+        /// Compares to semantic versions by precedence. This does the same as a Equals, but ignores the build information.
+        /// 
+        /// The semantic version.
+        /// true if the version precedence matches.
+        public bool PrecedenceMatches(SemVersion other)
+        {
+            return CompareByPrecedence(other) == 0;
+        }
+
+        /// 
+        /// Compares to semantic versions by precedence. This does the same as a Equals, but ignores the build information.
+        /// 
+        /// The semantic version.
+        /// 
+        /// A value that indicates the relative order of the objects being compared. 
+        /// The return value has these meanings: Value Meaning Less than zero 
+        ///  This instance precedes  in the version precedence.
+        ///  Zero This instance has the same precedence as . i
+        ///  Greater than zero This instance has creater precedence as .
+        /// 
+        public int CompareByPrecedence(SemVersion other)
+        {
+            if (ReferenceEquals(other, null))
+                return 1;
+
+            var r = this.Major.CompareTo(other.Major);
+            if (r != 0) return r;
+
+            r = this.Minor.CompareTo(other.Minor);
+            if (r != 0) return r;
+
+            r = this.Patch.CompareTo(other.Patch);
+            if (r != 0) return r;
+
+            r = CompareComponent(this.Prerelease, other.Prerelease, true);
+            return r;
+        }
+
+        static int CompareComponent(string a, string b, bool lower = false)
+        {
+            var aEmpty = String.IsNullOrEmpty(a);
+            var bEmpty = String.IsNullOrEmpty(b);
+            if (aEmpty && bEmpty)
+                return 0;
+
+            if (aEmpty)
+                return lower ? 1 : -1;
+            if (bEmpty)
+                return lower ? -1 : 1;
+
+            var aComps = a.Split('.');
+            var bComps = b.Split('.');
+
+            var minLen = Math.Min(aComps.Length, bComps.Length);
+            for (int i = 0; i < minLen; i++)
+            {
+                var ac = aComps[i];
+                var bc = bComps[i];
+                int anum, bnum;
+                var isanum = Int32.TryParse(ac, out anum);
+                var isbnum = Int32.TryParse(bc, out bnum);
+                int r;
+                if (isanum && isbnum)
+                {
+                    r = anum.CompareTo(bnum);
+                    if (r != 0) return anum.CompareTo(bnum);
+                }
+                else
+                {
+                    if (isanum)
+                        return -1;
+                    if (isbnum)
+                        return 1;
+                    r = String.CompareOrdinal(ac, bc);
+                    if (r != 0)
+                        return r;
+                }
+            }
+
+            return aComps.Length.CompareTo(bComps.Length);
+        }
+
+        /// 
+        /// Determines whether the specified  is equal to this instance.
+        /// 
+        /// The  to compare with this instance.
+        /// 
+        ///   true if the specified  is equal to this instance; otherwise, false.
+        /// 
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(obj, null))
+                return false;
+
+            if (ReferenceEquals(this, obj))
+                return true;
+
+            var other = (SemVersion)obj;
+
+            return this.Major == other.Major &&
+                this.Minor == other.Minor &&
+                this.Patch == other.Patch &&
+                string.Equals(this.Prerelease, other.Prerelease, StringComparison.Ordinal) &&
+                string.Equals(this.Build, other.Build, StringComparison.Ordinal);
+        }
+
+        /// 
+        /// Returns a hash code for this instance.
+        /// 
+        /// 
+        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+        /// 
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int result = this.Major.GetHashCode();
+                result = result * 31 + this.Minor.GetHashCode();
+                result = result * 31 + this.Patch.GetHashCode();
+                result = result * 31 + this.Prerelease.GetHashCode();
+                result = result * 31 + this.Build.GetHashCode();
+                return result;
+            }
+        }
+
+#if !NETSTANDARD
+        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
+        public void GetObjectData(SerializationInfo info, StreamingContext context)
+        {
+            if (info == null) throw new ArgumentNullException("info");
+            info.AddValue("SemVersion", ToString());
+        }
+#endif
+
+        /// 
+        /// The override of the equals operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is equal to right true, else false.
+        public static bool operator ==(SemVersion left, SemVersion right)
+        {
+            if(ReferenceEquals(right, null)) {
+                return ReferenceEquals(left, null);
+            }
+            if(ReferenceEquals(left, null)) {
+                return false;
+            }
+            return left.PrecedenceMatches(right);
+        }
+
+        /// 
+        /// The override of the un-equal operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is not equal to right true, else false.
+        public static bool operator !=(SemVersion left, SemVersion right)
+        {
+            return !(left == right);
+        }
+
+        /// 
+        /// The override of the greater operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is greater than right true, else false.
+        public static bool operator >(SemVersion left, SemVersion right)
+        {
+            return left.CompareByPrecedence(right) > 0;
+        }
+
+        /// 
+        /// The override of the greater than or equal operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is greater than or equal to right true, else false.
+        public static bool operator >=(SemVersion left, SemVersion right)
+        {
+            return left == right || left > right;
+        }
+
+        /// 
+        /// The override of the less operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is less than right true, else false.
+        public static bool operator <(SemVersion left, SemVersion right)
+        {
+            return left.CompareByPrecedence(right) < 0;
+        }
+
+        /// 
+        /// The override of the less than or equal operator. 
+        /// 
+        /// The left value.
+        /// The right value.
+        /// If left is less than or equal to right true, else false.
+        public static bool operator <=(SemVersion left, SemVersion right)
+        {
+            return left == right || left < right;
+        }
+    }
+}
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs.meta
new file mode 100644
index 0000000..cc7e5d9
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 0b49a1188451e7745af9f636d854efc8
+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/Versioning/SemVer.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window.meta b/Packages/com.singularitygroup.hotreload/Editor/Window.meta
new file mode 100644
index 0000000..710dd15
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e1826b88dea6aa446a9bc22bc0140c22
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI.meta
new file mode 100644
index 0000000..5cbd648
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dddc1cae3f951f84da98305ec6228f25
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons.meta
new file mode 100644
index 0000000..4d51a80
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 86f1446dfdbc2a94aac993437231aaa4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs
new file mode 100644
index 0000000..3ecfc20
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs
@@ -0,0 +1,42 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace SingularityGroup.HotReload.Editor {
+    internal class OpenDialogueButton : IGUIComponent {
+        public readonly string text;
+        public readonly string url;
+        public readonly string title;
+        public readonly string message;
+        public readonly string ok;
+        public readonly string cancel;
+        
+        public OpenDialogueButton(string text, string url, string title, string message, string ok, string cancel) {
+            this.text = text;
+            this.url = url;
+            this.title = title;
+            this.message = message;
+            this.ok = ok;
+            this.cancel = cancel;
+        }
+
+        public void OnGUI() {
+             Render(text, url, title, message, ok, cancel);
+        }
+
+        public static void Render(string text, string url, string title, string message, string ok, string cancel) {
+            if (GUILayout.Button(new GUIContent(text.StartsWith(" ") ? text : " " + text))) {
+                if (EditorUtility.DisplayDialog(title, message, ok, cancel)) {
+                    Application.OpenURL(url);
+                }
+            }
+        }
+        
+        public static void RenderRaw(Rect rect, string text, string url, string title, string message, string ok, string cancel, GUIStyle style = null) {
+            if (GUI.Button(rect, new GUIContent(text.StartsWith(" ") ? text : " " + text), style ?? GUI.skin.button)) {
+                if (EditorUtility.DisplayDialog(title, message, ok, cancel)) {
+                    Application.OpenURL(url);
+                }
+            }
+        }
+    }
+}
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs.meta
new file mode 100644
index 0000000..6018d35
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 97ca8174f0514e8e9ee5d4be26ed8078
+timeCreated: 1674416481
+AssetOrigin:
+  serializedVersion: 1
+  productId: 254358
+  packageName: Hot Reload | Edit Code Without Compiling
+  packageVersion: 1.13.17
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs
new file mode 100644
index 0000000..0f1edcc
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs
@@ -0,0 +1,29 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace SingularityGroup.HotReload.Editor {
+    internal class OpenURLButton : IGUIComponent {
+        public readonly string text;
+        public readonly string url;
+        public OpenURLButton(string text, string url) {
+            this.text = text;
+            this.url = url;
+        }
+
+        public void OnGUI() {
+            Render(text, url);
+        }
+
+        public static void Render(string text, string url) {
+            if (GUILayout.Button(new GUIContent(text.StartsWith(" ") ? text : " " + text))) {
+                Application.OpenURL(url);
+            }
+        }
+        
+        public static void RenderRaw(Rect rect, string text, string url, GUIStyle style = null) {
+            if (GUI.Button(rect, new GUIContent(text.StartsWith(" ") ? text : " " + text), style ?? GUI.skin.button)) {
+                Application.OpenURL(url);
+            }
+        }
+    }
+}
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs.meta
new file mode 100644
index 0000000..1150305
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: ef12252fc9d1f9f438cbd34cf8f7364b
+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/Window/GUI/Buttons/OpenURLButton.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs
new file mode 100644
index 0000000..d20fae9
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs
@@ -0,0 +1,116 @@
+using UnityEngine;
+
+namespace SingularityGroup.HotReload.Editor {
+    /// 
+    /// Create a new texture only once. Safe access to generated textures.
+    /// 
+    /// 
+    /// If 
+    internal static class EditorTextures {
+        private static Texture2D black;
+        private static Texture2D white;
+        private static Texture2D lightGray225;
+        private static Texture2D lightGray235;
+        private static Texture2D darkGray17;
+        private static Texture2D darkGray30;
+
+        // Texture2D.blackTexture doesn't render properly in Editor GUI.
+        public static Texture2D Black {
+            get {
+                if (!black) {
+                    black = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+                    
+                    var pixels = black.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(0, 0, 0, byte.MaxValue);
+                    }
+                    black.SetPixels32(pixels);
+                    black.Apply();
+                }
+                return black;
+            }
+        }
+        
+        // Texture2D.whiteTexture might not render properly in Editor GUI.
+        public static Texture2D White {
+            get {
+                
+                if (!white) {
+                    white = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+                    
+                    var pixels = white.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
+                    }
+                    white.SetPixels32(pixels);
+                    white.Apply();
+                }
+                return white;
+            }
+        }
+
+        public static Texture2D DarkGray17 {
+            get {
+                if (!darkGray17) {
+                    darkGray17 = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+                    
+                    var pixels = darkGray17.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(17, 17, 17, byte.MaxValue);
+                    }
+                    darkGray17.SetPixels32(pixels);
+                    darkGray17.Apply();
+                }
+                return darkGray17;
+            }
+        }
+        
+        public static Texture2D DarkGray40 {
+            get {
+                if (!darkGray30) {
+                    darkGray30 = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+                    
+                    var pixels = darkGray30.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(40, 40, 40, byte.MaxValue);
+                    }
+                    darkGray30.SetPixels32(pixels);
+                    darkGray30.Apply();
+                }
+                return darkGray30;
+            }
+        }
+
+        public static Texture2D LightGray238 {
+            get {
+                if (!lightGray235) {
+                    lightGray235 = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+                    
+                    var pixels = lightGray235.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(238, 238, 238, byte.MaxValue);
+                    }
+                    lightGray235.SetPixels32(pixels);
+                    lightGray235.Apply();
+                }
+                return lightGray235;
+            }
+        }
+
+        public static Texture2D LightGray225 {
+            get {
+                if (!lightGray225) {
+                    lightGray225 = new Texture2D(2, 2, TextureFormat.RGBA32, false);
+
+                    var pixels = lightGray225.GetPixels32();
+                    for (var i = 0; i < pixels.Length; i++) {
+                        pixels[i] = new Color32(225, 225, 225, byte.MaxValue);
+                    }
+                    lightGray225.SetPixels32(pixels);
+                    lightGray225.Apply();
+                }
+                return lightGray225;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs.meta
new file mode 100644
index 0000000..f96d000
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 9116854180be4f2b8fcc0422bcf570a5
+timeCreated: 1674127121
+AssetOrigin:
+  serializedVersion: 1
+  productId: 254358
+  packageName: Hot Reload | Edit Code Without Compiling
+  packageVersion: 1.13.17
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs
new file mode 100644
index 0000000..323ce59
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs
@@ -0,0 +1,5 @@
+namespace SingularityGroup.HotReload.Editor {
+    internal interface IGUIComponent {
+        void OnGUI();
+    }
+}
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs.meta
new file mode 100644
index 0000000..705aef6
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 893cb208871dab94488cb988920f0ebd
+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/Window/GUI/IGUIComponent.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options.meta
new file mode 100644
index 0000000..32dff3d
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2b3fa5ea1ed3545429de96b41801942f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
new file mode 100644
index 0000000..1374b2c
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
@@ -0,0 +1,49 @@
+using SingularityGroup.HotReload.Editor.Localization;
+using UnityEditor;
+
+namespace SingularityGroup.HotReload.Editor {
+    internal class AllowAndroidAppToMakeHttpRequestsOption : ProjectOptionBase {
+        public override string ShortSummary {
+            get {
+                return Translations.Settings.OptionAllowHttpRequests;
+            }
+        }
+
+        public override string Summary => ShortSummary;
+
+        public override bool GetValue(SerializedObject so) {
+            #if UNITY_2022_1_OR_NEWER
+            // use PlayerSettings as the source of truth 
+            return PlayerSettings.insecureHttpOption != InsecureHttpOption.NotAllowed;
+            #else
+            return GetProperty(so).boolValue;
+            #endif
+        }
+
+        public override string ObjectPropertyName =>
+            nameof(HotReloadSettingsObject.AllowAndroidAppToMakeHttpRequests);
+
+        public override void SetValue(SerializedObject so, bool value) {
+            base.SetValue(so, value);
+
+            // Enabling on Unity 2022 or newer → set the Unity option to ‘Development Builds only’
+            #if UNITY_2022_1_OR_NEWER
+            var notAllowed = PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed;
+            if (value) {
+                // user chose to enable it
+                if (notAllowed) {
+                    PlayerSettings.insecureHttpOption = InsecureHttpOption.DevelopmentOnly;
+                }
+            } else {
+                // user chose to disable it
+                PlayerSettings.insecureHttpOption = InsecureHttpOption.NotAllowed;
+            }
+            #endif
+        }
+
+        public override void InnerOnGUI(SerializedObject so) {
+            var description = Translations.Settings.OptionAllowHttpRequestsDescription;
+            EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs.meta
new file mode 100644
index 0000000..386aa56
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 0a7442cee510ab4498ca2a846e0c4e92
+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/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
+  uploadId: 870414
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base.meta
new file mode 100644
index 0000000..a16bb70
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bb8474c37f13d704d96b43e0f681680d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs
new file mode 100644
index 0000000..8cfa457
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs
@@ -0,0 +1,57 @@
+using UnityEditor;
+
+namespace SingularityGroup.HotReload.Editor {
+    /// 
+    /// An option stored inside the current Unity project.
+    /// 
+    internal abstract class ProjectOptionBase : IOption, ISerializedProjectOption {
+        public abstract string ShortSummary { get; }
+        public abstract string Summary { get; }
+
+        public virtual bool GetValue(SerializedObject so) {
+            return so.FindProperty(ObjectPropertyName).boolValue;
+        }
+
+        protected SerializedProperty GetProperty(SerializedObject so) {
+            return so.FindProperty(ObjectPropertyName);
+        }
+        
+        public virtual void SetValue(SerializedObject so, bool value) {
+            so.FindProperty(ObjectPropertyName).boolValue = value;
+        }
+
+        public virtual void InnerOnGUI(SerializedObject so) { }
+
+        public abstract string ObjectPropertyName { get; }
+
+        /// 
+        /// Override this if your option is not needed for on-device Hot Reload to work.
+ /// (by default, a project option must be true for Hot Reload to work) + ///
+ public virtual bool IsRequiredForBuild() { + return true; + } + } + + /// + /// An option that is stored on the user's computer (shared between Unity projects). + /// + internal abstract class ComputerOptionBase : IOption { + public abstract string ShortSummary { get; } + public abstract string Summary { get; } + + public abstract bool GetValue(); + + /// Uses for storing the value on the user's computer. + public virtual void SetValue(bool value) { } + + public bool GetValue(SerializedObject so) => GetValue(); + + public virtual void SetValue(SerializedObject so, bool value) => SetValue(value); + + void IOption.InnerOnGUI(SerializedObject so) { + InnerOnGUI(); + } + public virtual void InnerOnGUI() { } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs.meta new file mode 100644 index 0000000..c3e72b4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dab8ef53c2ee30a40ab6a7e4abd1260c +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/Window/GUI/Options/Base/HotReloadOptionBase.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs new file mode 100644 index 0000000..e68f3a1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs @@ -0,0 +1,34 @@ +using UnityEditor; + +namespace SingularityGroup.HotReload.Editor { + public interface IOption { + string ShortSummary { get; } + string Summary { get; } + + /// The wrapped by SerializedObject + bool GetValue(SerializedObject so); + + /// + /// Handle the new value. + /// + /// + /// Note: caller must skip calling this if value same as GetValue! + /// + /// The wrapped by SerializedObject + /// + void SetValue(SerializedObject so, bool value); + + /// The wrapped by SerializedObject + void InnerOnGUI(SerializedObject so); + } + + /// + /// An option scoped to the current Unity project. + /// + /// + /// These options are intended to be shared with collaborators and used by Unity Player builds. + /// + public interface ISerializedProjectOption { + string ObjectPropertyName { get; } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs.meta new file mode 100644 index 0000000..236488e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0a626aa97160471f85de4646a634bdf1 +timeCreated: 1674574633 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs new file mode 100644 index 0000000..732946b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs @@ -0,0 +1,70 @@ +using System; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor; + +namespace SingularityGroup.HotReload.Editor { + internal sealed class ExposeServerOption : ComputerOptionBase { + + public override string ShortSummary => Translations.Settings.OptionExposeServerShort; + public override string Summary => Translations.Settings.OptionExposeServerFull; + + public override void InnerOnGUI() { + string description; + if (GetValue()) { + description = Translations.Settings.OptionExposeServerDescriptionEnabled; + } else { + description = Translations.Settings.OptionExposeServerDescriptionDisabled; + } + EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle); + } + + public override bool GetValue() { + return HotReloadPrefs.ExposeServerToLocalNetwork; + } + + public override void SetValue(SerializedObject so, bool val) { + // AllowAndroidAppToMakeHttpRequestsOption + if (val == HotReloadPrefs.ExposeServerToLocalNetwork) { + return; + } + + HotReloadPrefs.ExposeServerToLocalNetwork = val; + if (val) { + // they allowed this one for mobile builds, so now we allow everything else needed for player build to work with HR + new AllowAndroidAppToMakeHttpRequestsOption().SetValue(so, true); + } + RunTask(() => { + RunOnMainThreadSync(() => { + var isRunningResult = ServerHealthCheck.I.IsServerHealthy; + if (isRunningResult) { + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleHotReload, + string.Format(Translations.Dialogs.DialogMessageRestartExposeServer, Summary), + Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + CodePatcher.I.ClearPatchedMethods(); + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + }); + }); + } + + void RunTask(Action action) { + var token = HotReloadWindow.Current.cancelToken; + Task.Run(() => { + if (token.IsCancellationRequested) return; + try { + action(); + } catch (Exception ex) { + ThreadUtility.LogException(ex, token); + } + }, token); + } + + void RunOnMainThreadSync(Action action) { + ThreadUtility.RunOnMainThread(action, HotReloadWindow.Current.cancelToken); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs.meta new file mode 100644 index 0000000..aea65a1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5ab0973d3ae1275469237480381842c0 +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/Window/GUI/Options/ExposeServerOption.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs new file mode 100644 index 0000000..320faf9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs @@ -0,0 +1,25 @@ +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor; + +namespace SingularityGroup.HotReload.Editor { + internal class IncludeInBuildOption : ProjectOptionBase, ISerializedProjectOption { + static IncludeInBuildOption _I; + public static IncludeInBuildOption I = _I ?? (_I = new IncludeInBuildOption()); + public override string ShortSummary => Translations.Settings.OptionIncludeInBuild; + public override string Summary => ShortSummary; + + public override string ObjectPropertyName => + nameof(HotReloadSettingsObject.IncludeInBuild); + + public override void InnerOnGUI(SerializedObject so) { + string description; + if (GetValue(so)) { + description = Translations.Settings.OptionIncludeInBuildDescriptionEnabled; + } else { + description = Translations.Settings.OptionIncludeInBuildDescriptionDisabled; + } + description += Translations.Settings.OptionIncludeInBuildDescriptionSuffix; + EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle); + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs.meta new file mode 100644 index 0000000..2ab1317 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 39ed4f822bcd81340bdf7189b3bc5016 +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/Window/GUI/Options/IncludeInBuildOption.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs.meta new file mode 100644 index 0000000..b777231 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c0f7811020465d46bcd0305e2f83e8a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base.meta new file mode 100644 index 0000000..9cec40c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58d14712b7ef14540ba4817a5ef873a6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs new file mode 100644 index 0000000..7a64888 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs @@ -0,0 +1,33 @@ + +using UnityEditor; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + internal abstract class HotReloadTabBase : IGUIComponent { + protected readonly HotReloadWindow _window; + + public string Title { get; } + public Texture Icon { get; } + public string Tooltip { get; } + + public HotReloadTabBase(HotReloadWindow window, string title, Texture iconImage, string tooltip) { + _window = window; + + Title = title; + Icon = iconImage; + Tooltip = tooltip; + } + + public HotReloadTabBase(HotReloadWindow window, string title, string iconName, string tooltip) : + this(window, title, EditorGUIUtility.IconContent(iconName).image, tooltip) { + } + + protected void Repaint() { + _window.Repaint(); + } + + public virtual void Update() { } + + public abstract void OnGUI(); + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs.meta new file mode 100644 index 0000000..37db52d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c2c79b82bd9636d499449f91f93fae2a +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/Window/GUI/Tabs/Base/HotReloadTabBase.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers.meta new file mode 100644 index 0000000..0c5c6ba --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a089a7225d904b00b2893a34b514ad28 +timeCreated: 1689791626 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs new file mode 100644 index 0000000..b282964 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Localization; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + internal enum RedeemStage { + None, + Registration, + Redeem, + Login + } + + // IMPORTANT: don't rename + internal enum RegistrationOutcome { + None, + Indie, + Business, + } + + internal class RedeemLicenseHelper { + public static readonly RedeemLicenseHelper I = new RedeemLicenseHelper(); + + private string _pendingCompanySize; + private string _pendingInvoiceNumber; + private string _pendingRedeemEmail; + + private static string registerFlagPath = PackageConst.LibraryCachePath + "/registerFlag.txt"; + public static string registerOutcomePath = PackageConst.LibraryCachePath + "/registerOutcome.txt"; + + public RedeemStage RedeemStage { get; private set; } + public RegistrationOutcome RegistrationOutcome { get; private set; } + public bool RegistrationRequired => RedeemStage != RedeemStage.None; + + private string status; + private string error; + + const string statusSuccess = "success"; + const string statusAlreadyClaimed = "already redeemed by this user/device"; + + private GUILayoutOption[] secondaryButtonLayoutOptions = new[] { GUILayout.MaxWidth(100) }; + + private bool requestingRedeem; + private HttpClient redeemClient; + const string redeemUrl = "https://vmhzj6jonn3qy7hk7tx7levpli0bstpj.lambda-url.us-east-1.on.aws/redeem"; + + public RedeemLicenseHelper() { + if (File.Exists(registerFlagPath)) { + RedeemStage = RedeemStage.Registration; + } + try { + if (File.Exists(registerOutcomePath)) { + RegistrationOutcome outcome; + if (Enum.TryParse(File.ReadAllText(registerOutcomePath), out outcome)) { + RegistrationOutcome = outcome; + } + } + } catch (Exception e) { + Log.Warning(Translations.Errors.WarningFailedDeterminingRegistration, e.GetType().Name, e.Message); + } + } + + public void RenderStage(HotReloadRunTabState state) { + if (state.redeemStage == RedeemStage.Registration) { + RenderRegistration(); + } else if (state.redeemStage == RedeemStage.Redeem) { + RenderRedeem(); + } else if (state.redeemStage == RedeemStage.Login) { + RenderLogin(state); + } + } + + private void RenderRegistration() { + var message = PackageConst.IsAssetStoreBuild + ? Translations.Registration.MessageRegistrationProUsers + : Translations.Registration.MessageRegistrationLicensingModel; + if (error != null) { + EditorGUILayout.HelpBox(error, MessageType.Warning); + } else { + EditorGUILayout.HelpBox(message, MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField(Translations.Common.LabelCompanySize); + GUI.SetNextControlName("company_size"); + _pendingCompanySize = EditorGUILayout.TextField(_pendingCompanySize)?.Trim(); + EditorGUILayout.Space(); + + if (GUILayout.Button(Translations.Common.ButtonProceed)) { + int companySize; + if (!int.TryParse(_pendingCompanySize, out companySize)) { + error = Translations.Errors.ErrorEnterNumber; + } else { + error = null; + HandleRegistration(companySize); + } + } + } + + void HandleRegistration(int companySize) { + RequestHelper.RequestEditorEvent(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Licensing, StatEventType.Register), new EditorExtraData { { StatKey.CompanySize, companySize } }); + if (companySize > 10) { + FinishRegistration(RegistrationOutcome.Business); + EditorCodePatcher.DownloadAndRun().Forget(); + } else if (PackageConst.IsAssetStoreBuild) { + SwitchToStage(RedeemStage.Redeem); + } else { + FinishRegistration(RegistrationOutcome.Indie); + EditorCodePatcher.DownloadAndRun().Forget(); + } + } + + private void RenderRedeem() { + if (error != null) { + EditorGUILayout.HelpBox(error, MessageType.Warning); + } else { + EditorGUILayout.HelpBox(Translations.Registration.MessageRedeemInstructions, MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField(Translations.Common.LabelInvoiceNumber); + GUI.SetNextControlName("invoice_number"); + _pendingInvoiceNumber = EditorGUILayout.TextField(_pendingInvoiceNumber ?? HotReloadPrefs.RedeemLicenseInvoice)?.Trim(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField(Translations.Common.LabelEmail); + GUI.SetNextControlName("email_redeem"); + _pendingRedeemEmail = EditorGUILayout.TextField(_pendingRedeemEmail ?? HotReloadPrefs.RedeemLicenseEmail); + EditorGUILayout.Space(); + + using (new EditorGUI.DisabledScope(requestingRedeem)) { + if (GUILayout.Button(Translations.Common.ButtonRedeem, HotReloadRunTab.bigButtonHeight)) { + RedeemLicense(email: _pendingRedeemEmail, invoiceNumber: _pendingInvoiceNumber).Forget(); + } + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Translations.Common.ButtonSkip, secondaryButtonLayoutOptions)) { + SwitchToStage(RedeemStage.Login); + } + GUILayout.FlexibleSpace(); + } + } + + async Task RedeemLicense(string email, string invoiceNumber) { + string validationError; + if (string.IsNullOrEmpty(invoiceNumber)) { + validationError = Translations.Errors.ErrorEnterInvoiceNumber; + } else { + validationError = HotReloadRunTab.ValidateEmail(email); + } + if (validationError != null) { + error = validationError; + return; + } + var resp = await RequestRedeem(email: email, invoiceNumber: invoiceNumber); + status = resp?.status; + if (status != null) { + if (status != statusSuccess && status != statusAlreadyClaimed) { + Log.Error(Translations.Errors.WarningRedeemStatusUnknown); + error = Translations.Registration.UnknownRedeemError; + } else { + HotReloadPrefs.RedeemLicenseEmail = email; + HotReloadPrefs.RedeemLicenseInvoice = invoiceNumber; + // prepare data for login screen + HotReloadPrefs.LicenseEmail = email; + HotReloadPrefs.LicensePassword = null; + + SwitchToStage(RedeemStage.Login); + } + } else if (resp?.error != null) { + Log.Warning(Translations.Errors.WarningRedeemingLicenseFailed, resp.error); + error = GetPrettyError(resp); + } else { + Log.Warning(Translations.Errors.WarningRedeemUnknownError); + error = Translations.Registration.UnknownRedeemError; + } + } + + string GetPrettyError(RedeemResponse response) { + var err = response?.error; + if (err == null) { + return Translations.Registration.UnknownRedeemError; + } + if (err.Contains("Invalid email")) { + return Translations.Errors.ErrorInvalidEmailAddress; + } else if (err.Contains("License invoice already redeemed")) { + return Translations.Errors.ErrorLicenseInvoiceRedeemed; + } else if (err.Contains("Different license already redeemed by given email")) { + return Translations.Errors.ErrorEmailAlreadyUsed; + } else if (err.Contains("Invoice not found")) { + return Translations.Errors.ErrorInvoiceNotFound; + } else if (err.Contains("Invoice refunded")) { + return Translations.Errors.ErrorInvoiceRefunded; + } else { + return Translations.Registration.UnknownRedeemError; + } + } + + async Task RequestRedeem(string email, string invoiceNumber) { + requestingRedeem = true; + await ThreadUtility.SwitchToThreadPool(); + try { + redeemClient = redeemClient ?? (redeemClient = HttpClientUtils.CreateHttpClient()); + var input = new Dictionary { + { "email", email }, + { "invoice", invoiceNumber } + }; + var content = new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, "application/json"); + using (var resp = await redeemClient.PostAsync(redeemUrl, content, HotReloadWindow.Current.cancelToken).ConfigureAwait(false)) { + if (resp.StatusCode != HttpStatusCode.OK) { + return new RedeemResponse(null, string.Format(Translations.Errors.ErrorRedeemRequestFailed, (int)resp.StatusCode, resp.ReasonPhrase)); + } + var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + try { + return JsonConvert.DeserializeObject(str); + } catch (Exception ex) { + return new RedeemResponse(null, string.Format(Translations.Errors.ErrorFailedDeserializingRedeem, ex.GetType().Name, ex.Message)); + } + } + } catch (WebException ex) { + return new RedeemResponse(null, string.Format(Translations.Errors.ErrorRedeemingWebException, ex.Message)); + } finally { + requestingRedeem = false; + } + } + + private class RedeemResponse { + public string status; + public string error; + + public RedeemResponse(string status, string error) { + this.status = status; + this.error = error; + } + } + + private void RenderLogin(HotReloadRunTabState state) { + if (status == statusSuccess) { + EditorGUILayout.HelpBox(Translations.Registration.MessageRedeemSuccess, MessageType.Info); + } else if (status == statusAlreadyClaimed) { + EditorGUILayout.HelpBox(Translations.Registration.MessageRedeemAlreadyClaimed, MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + HotReloadRunTab.RenderLicenseInnerPanel(state, renderLogout: false); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Translations.Common.ButtonGoBack, secondaryButtonLayoutOptions)) { + SwitchToStage(RedeemStage.Redeem); + } + GUILayout.FlexibleSpace(); + } + } + + public void StartRegistration() { + // ReSharper disable once AssignNullToNotNullAttribute + Directory.CreateDirectory(Path.GetDirectoryName(registerFlagPath)); + using (File.Create(registerFlagPath)) { + } + RedeemStage = RedeemStage.Registration; + RegistrationOutcome = RegistrationOutcome.None; + } + + public void FinishRegistration(RegistrationOutcome outcome) { + // ReSharper disable once AssignNullToNotNullAttribute + Directory.CreateDirectory(Path.GetDirectoryName(registerFlagPath)); + File.WriteAllText(registerOutcomePath, outcome.ToString()); + File.Delete(registerFlagPath); + RegistrationOutcome = outcome; + SwitchToStage(RedeemStage.None); + Cleanup(); + } + + void SwitchToStage(RedeemStage stage) { + // remove focus so that the input field re-renders + GUI.FocusControl(null); + RedeemStage = stage; + } + + void Cleanup() { + redeemClient?.Dispose(); + redeemClient = null; + _pendingCompanySize = null; + _pendingInvoiceNumber = null; + _pendingRedeemEmail = null; + status = null; + error = null; + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs.meta new file mode 100644 index 0000000..5386a59 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ad73f74d3c494c02aae937e2dfa305a2 +timeCreated: 1689791373 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs new file mode 100644 index 0000000..77b4a0c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor; +using UnityEngine; +using System.Threading.Tasks; +using System.IO; +using SingularityGroup.HotReload.Newtonsoft.Json; +using SingularityGroup.HotReload.EditorDependencies; + +namespace SingularityGroup.HotReload.Editor { + internal struct HotReloadAboutTabState { + public readonly bool logsFodlerExists; + public readonly IReadOnlyList changelog; + public readonly bool loginRequired; + public readonly bool hasTrialLicense; + public readonly bool hasPayedLicense; + + public HotReloadAboutTabState( + bool logsFodlerExists, + IReadOnlyList changelog, + bool loginRequired, + bool hasTrialLicense, + bool hasPayedLicense + ) { + this.logsFodlerExists = logsFodlerExists; + this.changelog = changelog; + this.loginRequired = loginRequired; + this.hasTrialLicense = hasTrialLicense; + this.hasPayedLicense = hasPayedLicense; + } + } + + internal class HotReloadAboutTab : HotReloadTabBase { + internal static readonly OpenURLButton seeMore = new OpenURLButton(Translations.About.ButtonSeeMore, Constants.ChangelogURL); + internal static readonly OpenDialogueButton manageLicenseButton = new OpenDialogueButton(Translations.About.ButtonManageLicense, Constants.ManageLicenseURL, Translations.About.ButtonManageLicense, Translations.Dialogs.DialogManageLicenseMessage, Translations.Common.ButtonOpenInBrowser, Translations.Common.ButtonCancel); + internal static readonly OpenDialogueButton manageAccountButton = new OpenDialogueButton(Translations.About.ButtonManageAccount, Constants.ManageAccountURL, Translations.About.ButtonManageAccount, Translations.Dialogs.DialogManageAccountMessage, Translations.Common.ButtonOpenInBrowser, Translations.Common.ButtonCancel); + internal static readonly OpenURLButton contactButton = new OpenURLButton(Translations.About.ButtonContact, Constants.ContactURL); + internal static readonly OpenURLButton discordButton = new OpenURLButton(Translations.About.ButtonJoinDiscord, Constants.DiscordInviteUrl); + internal static readonly OpenDialogueButton reportIssueButton = new OpenDialogueButton(Translations.About.ButtonReportIssue, Constants.ReportIssueURL, Translations.About.ButtonReportIssue, Translations.Dialogs.DialogReportIssueMessage, Translations.Common.ButtonOpenInBrowser, Translations.Common.ButtonCancel); + + private Vector2 _changelogScroll; + private IReadOnlyList _changelog = new List(); + private bool _requestedChangelog; + private int _changelogRequestAttempt; + private string _changelogDir = Path.Combine(PackageConst.LibraryCachePath, "changelog.json"); + public static string logsPath = Path.Combine(PackageConst.LibraryCachePath, "logs"); + + private static bool LatestChangelogLoaded(IReadOnlyList changelog) { + return changelog.Any() && changelog[0].versionNum == PackageUpdateChecker.lastRemotePackageVersion; + } + + private async Task FetchChangelog() { + if(!_changelog.Any()) { + var file = new FileInfo(_changelogDir); + if (file.Exists) { + await Task.Run(() => { + var bytes = File.ReadAllText(_changelogDir); + _changelog = JsonConvert.DeserializeObject>(bytes); + }); + } + } + if (_requestedChangelog || LatestChangelogLoaded(_changelog)) { + return; + } + _requestedChangelog = true; + try { + do { + var changelogRequestTimeout = ExponentialBackoff.GetTimeout(_changelogRequestAttempt); + _changelog = await RequestHelper.FetchChangelog() ?? _changelog; + if (LatestChangelogLoaded(_changelog)) { + await Task.Run(() => { + Directory.CreateDirectory(PackageConst.LibraryCachePath); + File.WriteAllText(_changelogDir, JsonConvert.SerializeObject(_changelog)); + }); + Repaint(); + return; + } + await Task.Delay(changelogRequestTimeout); + } while (_changelogRequestAttempt++ < 1000 && !LatestChangelogLoaded(_changelog)); + } catch { + // ignore + } finally { + _requestedChangelog = false; + } + } + + public HotReloadAboutTab(HotReloadWindow window) : base(window, Translations.About.AboutTitle, "_Help", Translations.About.AboutDescription) { } + + string GetRelativeDate(DateTime givenDate) { + const int second = 1; + const int minute = 60 * second; + const int hour = 60 * minute; + const int day = 24 * hour; + const int month = 30 * day; + + var ts = new TimeSpan(DateTime.UtcNow.Ticks - givenDate.Ticks); + var delta = Math.Abs(ts.TotalSeconds); + + if (delta < 24 * hour) + return Translations.About.AboutToday; + + if (delta < 48 * hour) + return Translations.About.AboutYesterday; + + if (delta < 30 * day) + return string.Format(Translations.About.AboutDaysAgo, ts.Days); + + if (delta < 12 * month) { + var months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); + return months <= 1 ? Translations.About.AboutOneMonthAgo : string.Format(Translations.About.AboutMonthsAgo, months); + } + var years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); + return years <= 1 ? Translations.About.AboutOneYearAgo : string.Format(Translations.About.AboutYearsAgo, years); + } + + void RenderVersion(ChangelogVersion version) { + var tempTextString = ""; + + //version number + EditorGUILayout.TextArea(version.versionNum, HotReloadWindowStyles.H1TitleStyle); + + //general info + if (version.generalInfo != null) { + EditorGUILayout.TextArea(version.generalInfo, HotReloadWindowStyles.H3TitleStyle); + } + + //features + if (version.features != null) { + EditorGUILayout.TextArea(Translations.About.AboutFeatures, HotReloadWindowStyles.H2TitleStyle); + tempTextString = ""; + foreach (var feature in version.features) { + tempTextString += "• " + feature + "\n"; + } + EditorGUILayout.TextArea(tempTextString, HotReloadWindowStyles.ChangelogPointerStyle); + } + + //improvements + if (version.improvements != null) { + EditorGUILayout.TextArea(Translations.About.AboutImprovements, HotReloadWindowStyles.H2TitleStyle); + tempTextString = ""; + foreach (var improvement in version.improvements) { + tempTextString += "• " + improvement + "\n"; + } + EditorGUILayout.TextArea(tempTextString, HotReloadWindowStyles.ChangelogPointerStyle); + } + + //fixes + if (version.fixes != null) { + EditorGUILayout.TextArea(Translations.About.AboutFixes, HotReloadWindowStyles.H2TitleStyle); + tempTextString = ""; + foreach (var fix in version.fixes) { + tempTextString += "• " + fix + "\n"; + } + EditorGUILayout.TextArea(tempTextString, HotReloadWindowStyles.ChangelogPointerStyle); + } + + //date + DateTime date; + if (DateTime.TryParseExact(version.date, "dd/MM/yyyy", null, DateTimeStyles.None, out date)) { + var relativeDate = GetRelativeDate(date); + GUILayout.TextArea(relativeDate, HotReloadWindowStyles.H3TitleStyle); + } + } + + void RenderChangelog() { + FetchChangelog().Forget(); + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.ShowChangeLog = EditorGUILayout.Foldout(HotReloadPrefs.ShowChangeLog, Translations.Miscellaneous.ChangelogTitle, true, HotReloadWindowStyles.FoldoutStyle); + if (!HotReloadPrefs.ShowChangeLog) { + return; + } + // changelog versions + var maxChangeLogs = 5; + var index = 0; + foreach (var version in currentState.changelog) { + index++; + if (index > maxChangeLogs) { + break; + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.ChangelogSectionInnerBox)) { + using (new EditorGUILayout.VerticalScope()) { + RenderVersion(version); + } + } + } + // see more button + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.ChangelogSectionInnerBox)) { + seeMore.OnGUI(); + } + } + } + } + + private Vector2 _aboutTabScrollPos; + + HotReloadAboutTabState currentState; + public override void OnGUI() { + // HotReloadAboutTabState ensures rendering is consistent between Layout and Repaint calls + // Without it errors like this happen: + // ArgumentException: Getting control 2's position in a group with only 2 controls when doing repaint + // See thread for more context: https://answers.unity.com/questions/17718/argumentexception-getting-control-2s-position-in-a.html + if (Event.current.type == EventType.Layout) { + currentState = new HotReloadAboutTabState( + logsFodlerExists: Directory.Exists(logsPath), + changelog: _changelog, + loginRequired: EditorCodePatcher.LoginNotRequired, + hasTrialLicense: _window.RunTab.TrialLicense, + hasPayedLicense: _window.RunTab.HasPayedLicense + ); + } + using (var scope = new EditorGUILayout.ScrollViewScope(_aboutTabScrollPos, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(Math.Max(HotReloadWindowStyles.windowScreenHeight, 800)), GUILayout.MaxWidth(Math.Max(HotReloadWindowStyles.windowScreenWidth, 800)))) { + _aboutTabScrollPos.x = scope.scrollPosition.x; + _aboutTabScrollPos.y = scope.scrollPosition.y; + + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.DynamicSectionHelpTab)) { + using (new EditorGUILayout.VerticalScope()) { + GUILayout.Space(10); + RenderLogButtons(); + + EditorGUILayout.Space(); + EditorGUILayout.HelpBox(string.Format(Translations.About.AboutVersionInfo, PackageConst.Version), MessageType.Info); + EditorGUILayout.Space(); + + RenderHelpButtons(); + + GUILayout.Space(15); + + try { + RenderChangelog(); + } catch { + // ignore + } + } + } + } + } + + void RenderHelpButtons() { + var labelRect = GUILayoutUtility.GetLastRect(); + using (new EditorGUILayout.HorizontalScope()) { + using (new EditorGUILayout.VerticalScope()) { + var buttonHeight = 19; + + var bigButtonRect = new Rect(labelRect.x + 3, labelRect.y + 5, labelRect.width - 6, buttonHeight); + OpenURLButton.RenderRaw(bigButtonRect, Translations.About.ButtonDocumentation, Constants.DocumentationURL, HotReloadWindowStyles.HelpTabButton); + + var firstLayerX = bigButtonRect.x; + var firstLayerY = bigButtonRect.y + buttonHeight + 3; + var firstLayerWidth = (int)((bigButtonRect.width / 2) - 3); + + var secondLayerX = firstLayerX + firstLayerWidth + 5; + var secondLayerY = firstLayerY + buttonHeight + 3; + var secondLayerWidth = bigButtonRect.width - firstLayerWidth - 5; + + using (new EditorGUILayout.HorizontalScope()) { + OpenURLButton.RenderRaw(new Rect { x = firstLayerX, y = firstLayerY, width = firstLayerWidth, height = buttonHeight }, contactButton.text, contactButton.url, HotReloadWindowStyles.HelpTabButton); + OpenURLButton.RenderRaw(new Rect { x = secondLayerX, y = firstLayerY, width = secondLayerWidth, height = buttonHeight }, Translations.About.ButtonUnityForum, Constants.ForumURL, HotReloadWindowStyles.HelpTabButton); + } + using (new EditorGUILayout.HorizontalScope()) { + OpenDialogueButton.RenderRaw(rect: new Rect { x = firstLayerX, y = secondLayerY, width = firstLayerWidth, height = buttonHeight }, text: reportIssueButton.text, url: reportIssueButton.url, title: reportIssueButton.title, message: reportIssueButton.message, ok: reportIssueButton.ok, cancel: reportIssueButton.cancel, style: HotReloadWindowStyles.HelpTabButton); + OpenURLButton.RenderRaw(new Rect { x = secondLayerX, y = secondLayerY, width = secondLayerWidth, height = buttonHeight }, discordButton.text, discordButton.url, HotReloadWindowStyles.HelpTabButton); + } + } + } + GUILayout.Space(80); + } + + void RenderLogButtons() { + if (currentState.logsFodlerExists) { + EditorGUILayout.Space(); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Translations.Common.ButtonOpenLogFile)) { + var mostRecentFile = LogsHelper.FindRecentLog(logsPath); + if (mostRecentFile == null) { + Log.Info(Translations.About.LogNoLogsFound); + } else { + try { + Process.Start($"\"{Path.Combine(logsPath, mostRecentFile)}\""); + } catch (Win32Exception e) { + // TODO: is this the same for chinese? + if (e.Message.Contains("Application not found")) { + try { + Process.Start("notepad.exe", $"\"{Path.Combine(logsPath, mostRecentFile)}\""); + } catch { + // Fallback to opening folder with all logs + Process.Start($"\"{logsPath}\""); + Log.Info(Translations.About.LogFailedOpeningLogFile); + } + } + } catch { + // Fallback to opening folder with all logs + Process.Start($"\"{logsPath}\""); + Log.Info(Translations.About.LogFailedOpeningLogFile); + } + } + } + if (GUILayout.Button(Translations.Common.ButtonBrowseAllLogs)) { + Process.Start($"\"{logsPath}\""); + } + EditorGUILayout.EndHorizontal(); + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs.meta new file mode 100644 index 0000000..3439e8d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7cf8e9ef1ab770249a4318e88e882a85 +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/Window/GUI/Tabs/HotReloadAboutTab.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs new file mode 100644 index 0000000..281c76f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs @@ -0,0 +1,50 @@ +using UnityEditor; +using UnityEngine; +using SingularityGroup.HotReload.Editor.Localization; + +namespace SingularityGroup.HotReload.Editor { + internal class HotReloadOptionsSection { + /// + /// Opening options tab does not automatically create the settings asset file. + /// - The Options UI shows defaults if the object asset doesn't exist. + /// - When a build starts, we also ensure the asset file exists. + /// + public void DrawGUI(SerializedObject so) { + so.Update(); // must update in-case asset was modified externally + + foreach (var option in HotReloadSettingsTab.allOptions) { + GUILayout.Space(4f); + DrawOption(option, so); + } + + // commit any changes to the underlying ScriptableObject + if (so.hasModifiedProperties) { + so.ApplyModifiedProperties(); + // Ensure asset file exists on disk, because we initially create it in memory (to provide the default values) + // This does not save the asset, user has to do that by saving assets in Unity (e.g. press hotkey Ctrl + S) + var target = so.targetObject as HotReloadSettingsObject; + if (target == null) { + Log.Warning(Translations.Errors.WarningUnexpectedSaveProblem); + } else { + // when one of the project options changed then we ensure the asset file exists. + HotReloadSettingsEditor.EnsureSettingsCreated(target); + } + } + } + + static void DrawOption(IOption option, SerializedObject so) { + EditorGUILayout.BeginVertical(HotReloadWindowStyles.BoxStyle); + + var before = option.GetValue(so); + var after = EditorGUILayout.BeginToggleGroup(new GUIContent(" " + option.Summary), before); + if (after != before) { + option.SetValue(so, after); + } + + option.InnerOnGUI(so); + + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.EndVertical(); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs.meta new file mode 100644 index 0000000..c370325 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 24379a407eff8494eac0f7841b70e574 +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/Window/GUI/Tabs/HotReloadOptionsSection.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs new file mode 100644 index 0000000..302e07f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs @@ -0,0 +1,1463 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.EditorDependencies; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEngine; +using Color = UnityEngine.Color; +using Task = System.Threading.Tasks.Task; +#if UNITY_2019_4_OR_NEWER +using Unity.CodeEditor; +#endif + +namespace SingularityGroup.HotReload.Editor { + internal class ErrorData { + public string fileName; + public string error; + public TextAsset file; + public int lineNumber; + public string stacktrace; + public string linkString; + private static string[] supportedPaths = new[] { Path.GetFullPath("Assets"), Path.GetFullPath("Plugins") }; + + public static ErrorData GetErrorData(string errorString) { + // Get the relevant file name + string stackTrace = errorString; + string fileName = null; + try { + int csIndex = 0; + int attempt = 0; + do { + csIndex = errorString.IndexOf(".cs", csIndex + 1, StringComparison.Ordinal); + if (csIndex == -1) { + break; + } + int fileNameStartIndex = csIndex - 1; + for (; fileNameStartIndex >= 0; fileNameStartIndex--) { + if (!char.IsLetter(errorString[fileNameStartIndex])) { + if (errorString.Contains("error CS")) { + fileName = errorString.Substring(fileNameStartIndex + 1, + csIndex - fileNameStartIndex + ".cs".Length - 1); + } else { + fileName = errorString.Substring(fileNameStartIndex, + csIndex - fileNameStartIndex + ".cs".Length); + } + break; + } + } + } while (attempt++ < 100 && fileName == null); + } catch { + // ignore + } + fileName = fileName ?? Translations.UI.TapToShowStacktrace; + + // Get the error + string error = (errorString.Contains("error CS") + ? Translations.UI.CompileErrorMessage + ", " + : Translations.UI.UnsupportedChangeMessage + ", ") + Translations.UI.TapHereToSeeMore; + int endOfError = errorString.IndexOf(". in ", StringComparison.Ordinal); + string specialChars = "\"'/\\"; + char[] characters = specialChars.ToCharArray(); + int specialChar = errorString.IndexOfAny(characters); + try { + if (errorString.Contains("error CS") ) { + error = errorString.Substring(errorString.IndexOf("error CS", StringComparison.Ordinal), errorString.Length - errorString.IndexOf("error CS", StringComparison.Ordinal)).Trim(); + using (StringReader reader = new StringReader(error)) { + string line; + while ((line = reader.ReadLine()) != null) { + error = line; + break; + } + } + } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && endOfError > 0) { + error = errorString.Substring("errors: ".Length, endOfError - "errors: ".Length).Trim(); + } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && specialChar > 0) { + error = errorString.Substring("errors: ".Length, specialChar - "errors: ".Length).Trim(); + } + } catch { + // ignore + } + + // Get relative path + TextAsset file = null; + try { + foreach (var path in supportedPaths) { + int lastprojectIndex = 0; + int attempt = 0; + while (attempt++ < 100 && !file) { + lastprojectIndex = errorString.IndexOf(path, lastprojectIndex + 1, StringComparison.Ordinal); + if (lastprojectIndex == -1) { + break; + } + var fullCsIndex = errorString.IndexOf(".cs", lastprojectIndex, StringComparison.Ordinal); + var l = fullCsIndex - lastprojectIndex + ".cs".Length; + if (l <= 0) { + continue; + } + var candidateAbsolutePath = errorString.Substring(lastprojectIndex, fullCsIndex - lastprojectIndex + ".cs".Length); + var candidateRelativePath = EditorCodePatcher.GetRelativePath(filespec: candidateAbsolutePath, folder: path); + file = AssetDatabase.LoadAssetAtPath(candidateRelativePath); + } + } + } catch { + // ignore + } + + // Get the line number + int lineNumber = 0; + try { + int lastIndex = 0; + int attempt = 0; + do { + lastIndex = errorString.IndexOf(fileName, lastIndex + 1, StringComparison.Ordinal); + if (lastIndex == -1) { + break; + } + var part = errorString.Substring(lastIndex + fileName.Length); + if (!part.StartsWith(errorString.Contains("error CS") ? "(" : ":", StringComparison.Ordinal) + || part.Length == 1 + || !char.IsDigit(part[1]) + ) { + continue; + } + int y = 1; + for (; y < part.Length; y++) { + if (!char.IsDigit(part[y])) { + break; + } + } + if (int.TryParse(part.Substring(1, errorString.Contains("error CS") ? y - 1 : y), out lineNumber)) { + break; + } + } while (attempt++ < 100); + } catch { + //ignore + } + + return new ErrorData() { + fileName = fileName, + error = error, + file = file, + lineNumber = lineNumber, + stacktrace = stackTrace, + linkString = lineNumber > 0 ? fileName + ":" + lineNumber : fileName + }; + } + + } + + internal struct HotReloadRunTabState { + public readonly bool spinnerActive; + public readonly string indicationIconPath; + public readonly bool requestingDownloadAndRun; + public readonly bool starting; + public readonly bool stopping; + public readonly bool running; + public readonly Tuple startupProgress; + public readonly string indicationStatusText; + public readonly LoginStatusResponse loginStatus; + public readonly bool downloadRequired; + public readonly bool downloadStarted; + public readonly bool requestingLoginInfo; + public readonly RedeemStage redeemStage; + public readonly int suggestionCount; + + public HotReloadRunTabState( + bool spinnerActive, + string indicationIconPath, + bool requestingDownloadAndRun, + bool starting, + bool stopping, + bool running, + Tuple startupProgress, + string indicationStatusText, + LoginStatusResponse loginStatus, + bool downloadRequired, + bool downloadStarted, + bool requestingLoginInfo, + RedeemStage redeemStage, + int suggestionCount + ) { + this.spinnerActive = spinnerActive; + this.indicationIconPath = indicationIconPath; + this.requestingDownloadAndRun = requestingDownloadAndRun; + this.starting = starting; + this.stopping = stopping; + this.running = running; + this.startupProgress = startupProgress; + this.indicationStatusText = indicationStatusText; + this.loginStatus = loginStatus; + this.downloadRequired = downloadRequired; + this.downloadStarted = downloadStarted; + this.requestingLoginInfo = requestingLoginInfo; + this.redeemStage = redeemStage; + this.suggestionCount = suggestionCount; + } + + public static HotReloadRunTabState Current => new HotReloadRunTabState( + spinnerActive: EditorIndicationState.SpinnerActive, + indicationIconPath: EditorIndicationState.IndicationIconPath, + requestingDownloadAndRun: EditorCodePatcher.RequestingDownloadAndRun, + starting: EditorCodePatcher.Starting, + stopping: EditorCodePatcher.Stopping, + running: EditorCodePatcher.Running, + startupProgress: EditorCodePatcher.StartupProgress, + indicationStatusText: EditorIndicationState.IndicationStatusText, + loginStatus: EditorCodePatcher.Status, + downloadRequired: EditorCodePatcher.DownloadRequired, + downloadStarted: EditorCodePatcher.DownloadStarted, + requestingLoginInfo: EditorCodePatcher.RequestingLoginInfo, + redeemStage: RedeemLicenseHelper.I.RedeemStage, + suggestionCount: HotReloadTimelineHelper.Suggestions.Count + ); + } + + internal struct LicenseErrorData { + public readonly string description; + public bool showBuyButton; + public string buyButtonText; + public readonly bool showLoginButton; + public readonly string loginButtonText; + public readonly bool showSupportButton; + public readonly string supportButtonText; + public readonly bool showManageLicenseButton; + public readonly string manageLicenseButtonText; + + public LicenseErrorData(string description, bool showManageLicenseButton = false, string manageLicenseButtonText = "", string loginButtonText = "", bool showSupportButton = false, string supportButtonText = "", bool showBuyButton = false, string buyButtonText = "", bool showLoginButton = false) { + this.description = description; + this.showManageLicenseButton = showManageLicenseButton; + this.manageLicenseButtonText = manageLicenseButtonText; + this.loginButtonText = loginButtonText; + this.showSupportButton = showSupportButton; + this.supportButtonText = supportButtonText; + this.showBuyButton = showBuyButton; + this.buyButtonText = buyButtonText; + this.showLoginButton = showLoginButton; + } + } + + internal class HotReloadRunTab : HotReloadTabBase { + private static string _pendingEmail; + private static string _pendingPassword; + private string _pendingPromoCode; + private bool _requestingActivatePromoCode; + + private static Tuple _activateInfoMessage; + + private HotReloadRunTabState currentState => _window.RunTabState; + // Has Indie or Pro license (even if not currenctly active) + public bool HasPayedLicense => currentState.loginStatus != null && (currentState.loginStatus.isIndieLicense || currentState.loginStatus.isBusinessLicense); + public bool TrialLicense => currentState.loginStatus != null && (currentState.loginStatus?.isTrial == true); + + private Vector2 _patchedMethodsScrollPos; + private Vector2 _runTabScrollPos; + + private string promoCodeError; + private MessageType promoCodeErrorType; + private bool promoCodeActivatedThisSession; + + public HotReloadRunTab(HotReloadWindow window) : base(window, Translations.UI.RunTabTitle, "forward", Translations.UI.RunTabTooltip) { } + + public override void OnGUI() { + using(new EditorGUILayout.VerticalScope()) { + OnGUICore(); + } + } + + internal static bool ShouldRenderConsumption(HotReloadRunTabState currentState) => (currentState.running && !currentState.starting && !currentState.stopping && currentState.loginStatus?.isLicensed != true && currentState.loginStatus?.isFree != true && !EditorCodePatcher.LoginNotRequired) && !(currentState.loginStatus == null || currentState.loginStatus.isFree); + + void OnGUICore() { + using (var scope = new EditorGUILayout.ScrollViewScope(_runTabScrollPos, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(Math.Max(HotReloadWindowStyles.windowScreenHeight, 800)), GUILayout.MaxWidth(Math.Max(HotReloadWindowStyles.windowScreenWidth, 800)))) { + _runTabScrollPos.x = scope.scrollPosition.x; + _runTabScrollPos.y = scope.scrollPosition.y; + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.DynamiSection)) { + if (HotReloadWindowStyles.windowScreenWidth > Constants.UpgradeLicenseNoteHideWidth + && HotReloadWindowStyles.windowScreenHeight > Constants.UpgradeLicenseNoteHideHeight + ) { + RenderUpgradeLicenseNote(currentState, HotReloadWindowStyles.UpgradeLicenseButtonStyle); + } + + var renderDebuggerInfo = Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled; + RenderIndicationPanel(!renderDebuggerInfo); + if (renderDebuggerInfo) { + RenderDebuggerAttachedInfo(false); + } + if (CanRenderBars(currentState)) { + RenderBars(currentState); + // clear red dot next time button shows + HotReloadState.ShowingRedDot = false; + } + } + } + + // At the end to not fuck up rendering https://answers.unity.com/questions/400454/argumentexception-getting-control-0s-position-in-a-1.html + var renderStart = !EditorCodePatcher.Running && !EditorCodePatcher.Starting && !currentState.requestingDownloadAndRun && currentState.redeemStage == RedeemStage.None; + var e = Event.current; + if (renderStart && e.type == EventType.KeyUp + && (e.keyCode == KeyCode.Return + || e.keyCode == KeyCode.KeypadEnter) + ) { + EditorCodePatcher.DownloadAndRun().Forget(); + } + } + + internal static void RenderUpgradeLicenseNote(HotReloadRunTabState currentState, GUIStyle style) { + var isIndie = RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Indie + || EditorCodePatcher.licenseType == UnityLicenseType.UnityPersonalPlus; + + if (RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Business + && currentState.loginStatus?.isBusinessLicense != true + && EditorCodePatcher.Running + && (PackageConst.IsAssetStoreBuild || HotReloadPrefs.RateAppShown) + ) { + // Warn asset store users they need to buy a business license + // Website users get reminded after using Hot Reload for 5+ days + RenderBusinessLicenseInfo(style); + } else if (isIndie + && HotReloadPrefs.RateAppShown + && !PackageConst.IsAssetStoreBuild + && EditorCodePatcher.Running + && currentState.loginStatus?.isBusinessLicense != true + && currentState.loginStatus?.isIndieLicense != true + ) { + // Reminder users they need to buy an indie license + RenderIndieLicenseInfo(style); + } + } + + internal static bool CanRenderBars(HotReloadRunTabState currentState) { + if (Debugger.IsAttached && !CodePatcher.I.debuggerCompatibilityEnabled) { + return false; + } + return HotReloadWindowStyles.windowScreenHeight > Constants.EventsListHideHeight + && HotReloadWindowStyles.windowScreenWidth > Constants.EventsListHideWidth + && !currentState.starting + && !currentState.stopping + && !currentState.requestingDownloadAndRun + ; + } + + static Texture2D GetFoldoutIcon(AlertEntry alertEntry) { + InvertibleIcon alertIcon = InvertibleIcon.FoldoutClosed; + if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) { + alertIcon = InvertibleIcon.FoldoutOpen; + } + return GUIHelper.GetInvertibleIcon(alertIcon); + } + + static void ToggleEntry(AlertEntry alertEntry) { + if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) { + HotReloadTimelineHelper.expandedEntries.Remove(alertEntry); + } else { + HotReloadTimelineHelper.expandedEntries.Add(alertEntry); + } + } + + static void RenderEntries(TimelineType timelineType) { + List alertEntries; + + alertEntries = timelineType == TimelineType.Suggestions ? HotReloadTimelineHelper.Suggestions : HotReloadTimelineHelper.EventsTimeline; + + bool skipChildren = false; + for (int i = 0; i < alertEntries.Count; i++) { + var alertEntry = alertEntries[i]; + if (i > HotReloadTimelineHelper.maxVisibleEntries && alertEntry.entryType != EntryType.Child) { + break; + } + if (timelineType != TimelineType.Suggestions) { + if (alertEntry.entryType != EntryType.Child + && !enabledFilters.Contains(alertEntry.alertType) + ) { + skipChildren = true; + continue; + } else if (alertEntry.entryType == EntryType.Child && skipChildren) { + continue; + } else { + skipChildren = false; + } + } + + EntryType entryType = alertEntry.entryType; + + string title = $" {alertEntry.title}{(!string.IsNullOrEmpty(alertEntry.shortDescription) ? $": {alertEntry.shortDescription}": "")}"; + Texture2D icon = null; + GUIStyle style; + if (entryType != EntryType.Child) { + icon = GUIHelper.GetLocalIcon(HotReloadTimelineHelper.alertIconString[alertEntry.iconType]); + } + if (entryType == EntryType.Child) { + style = HotReloadWindowStyles.ChildBarStyle; + } else if (entryType == EntryType.Foldout) { + style = HotReloadWindowStyles.FoldoutBarStyle; + } else { + style = HotReloadWindowStyles.BarStyle; + } + + Rect startRect; + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.Space(0); + Rect spaceRect = GUILayoutUtility.GetLastRect(); + // entry header foldout arrow + if (entryType == EntryType.Foldout) { + GUI.Label(new Rect(spaceRect.x + 3, spaceRect.y, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry))); + } else if (entryType == EntryType.Child) { + GUI.Label(new Rect(spaceRect.x + 26, spaceRect.y + 2, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry))); + } + // a workaround to limit the width of the label + GUILayout.Label(new GUIContent(""), style); + startRect = GUILayoutUtility.GetLastRect(); + GUI.Label(startRect, new GUIContent(title, icon), style); + } + + bool clickableDescription = (alertEntry.title == Translations.Utility.UnsupportedChange || alertEntry.title == Translations.Utility.CompileError || alertEntry.title == Translations.Timeline.EventTitleFailedApplyingPatch) && alertEntry.alertData.alertEntryType != AlertEntryType.InlinedMethod; + + if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry) || alertEntry.alertType == AlertType.CompileError) { + using (new EditorGUILayout.VerticalScope()) { + using (new EditorGUILayout.HorizontalScope()) { + using (new EditorGUILayout.VerticalScope(entryType == EntryType.Child ? HotReloadWindowStyles.ChildEntryBoxStyle : HotReloadWindowStyles.EntryBoxStyle)) { + if (alertEntry.alertType == AlertType.Suggestion || !clickableDescription) { + GUILayout.Label(alertEntry.description, HotReloadWindowStyles.LabelStyle); + } + if (alertEntry.actionData != null) { + alertEntry.actionData.Invoke(); + } + GUILayout.Space(5f); + } + } + } + } + + // remove button + if (timelineType == TimelineType.Suggestions && alertEntry.hasExitButton) { + var isClick = GUI.Button(new Rect(startRect.x + startRect.width - 20, startRect.y + 2, 20, 20), new GUIContent(GUIHelper.GetInvertibleIcon(InvertibleIcon.Close)), HotReloadWindowStyles.RemoveIconStyle); + if (isClick) { + HotReloadTimelineHelper.EventsTimeline.Remove(alertEntry); + var kind = HotReloadSuggestionsHelper.FindSuggestionKind(alertEntry); + if (kind != null) { + HotReloadSuggestionsHelper.SetSuggestionInactive((HotReloadSuggestionKind)kind); + if (kind == HotReloadSuggestionKind.EditorsWithoutHRRunning) { + HotReloadState.ShowedEditorsWithoutHR = true; + } + } + _instantRepaint = true; + } + } + + // Extend background to whole entry + var endRect = GUILayoutUtility.GetLastRect(); + if (GUI.Button(new Rect(startRect) { height = endRect.y - startRect.y + endRect.height}, new GUIContent(""), HotReloadWindowStyles.BarBackgroundStyle) && (entryType == EntryType.Child || entryType == EntryType.Foldout)) { + ToggleEntry(alertEntry); + } + + if (alertEntry.alertType != AlertType.Suggestion && HotReloadWindowStyles.windowScreenWidth > 400 && entryType != EntryType.Child) { + using (new EditorGUILayout.HorizontalScope()) { + var ago = (DateTime.Now - alertEntry.timestamp); + GUI.Label(new Rect(startRect.x + startRect.width - 60, startRect.y, 80, 20), ago.TotalMinutes < 1 ? "now" : $"{(ago.TotalHours > 1 ? $"{Math.Floor(ago.TotalHours)} h " : string.Empty)}{ago.Minutes} min", HotReloadWindowStyles.TimestampStyle); + } + } + + GUILayout.Space(1f); + } + if (timelineType != TimelineType.Suggestions && HotReloadTimelineHelper.GetRunTabTimelineEventCount() > 40) { + GUILayout.Space(3f); + GUILayout.Label(Constants.Only40EntriesShown, HotReloadWindowStyles.EmptyListText); + } + } + + private static List _enabledFilters; + private static List enabledFilters { + get { + if (_enabledFilters == null) { + _enabledFilters = new List(); + } + + if (HotReloadPrefs.RunTabUnsupportedChangesFilter && !_enabledFilters.Contains(AlertType.UnsupportedChange)) + _enabledFilters.Add(AlertType.UnsupportedChange); + if (!HotReloadPrefs.RunTabUnsupportedChangesFilter && _enabledFilters.Contains(AlertType.UnsupportedChange)) + _enabledFilters.Remove(AlertType.UnsupportedChange); + + if (HotReloadPrefs.RunTabCompileErrorFilter && !_enabledFilters.Contains(AlertType.CompileError)) + _enabledFilters.Add(AlertType.CompileError); + if (!HotReloadPrefs.RunTabCompileErrorFilter && _enabledFilters.Contains(AlertType.CompileError)) + _enabledFilters.Remove(AlertType.CompileError); + + if (HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter && !_enabledFilters.Contains(AlertType.PartiallySupportedChange)) + _enabledFilters.Add(AlertType.PartiallySupportedChange); + if (!HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter && _enabledFilters.Contains(AlertType.PartiallySupportedChange)) + _enabledFilters.Remove(AlertType.PartiallySupportedChange); + + if (HotReloadPrefs.RunTabUndetectedPatchesFilter && !_enabledFilters.Contains(AlertType.UndetectedChange)) + _enabledFilters.Add(AlertType.UndetectedChange); + if (!HotReloadPrefs.RunTabUndetectedPatchesFilter && _enabledFilters.Contains(AlertType.UndetectedChange)) + _enabledFilters.Remove(AlertType.UndetectedChange); + + if (HotReloadPrefs.RunTabAppliedPatchesFilter && !_enabledFilters.Contains(AlertType.AppliedChange)) + _enabledFilters.Add(AlertType.AppliedChange); + if (!HotReloadPrefs.RunTabAppliedPatchesFilter && _enabledFilters.Contains(AlertType.AppliedChange)) + _enabledFilters.Remove(AlertType.AppliedChange); + + return _enabledFilters; + } + } + + private Vector2 suggestionsScroll; + static GUILayoutOption[] timelineButtonOptions = new[] { GUILayout.Height(27), GUILayout.Width(100) }; + + internal static void RenderBars(HotReloadRunTabState currentState) { + if (currentState.suggestionCount > 0) { + GUILayout.Space(5f); + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.RunTabEventsSuggestionsFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsSuggestionsFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle); + GUILayout.Space(-23); + if (GUILayout.Button(string.Format(Translations.Timeline.LabelSuggestionsFormat, currentState.suggestionCount.ToString()), HotReloadWindowStyles.ClickableLabelBoldStyle, GUILayout.Height(27))) { + HotReloadPrefs.RunTabEventsSuggestionsFoldout = !HotReloadPrefs.RunTabEventsSuggestionsFoldout; + } + if (HotReloadPrefs.RunTabEventsSuggestionsFoldout) { + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Scroll)) { + RenderEntries(TimelineType.Suggestions); + } + } + } + } + } + GUILayout.Space(5f); + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.RunTabEventsTimelineFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsTimelineFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle); + GUILayout.Space(-23); + if (GUILayout.Button(Translations.Timeline.LabelTimeline, HotReloadWindowStyles.ClickableLabelBoldStyle, timelineButtonOptions)) { + HotReloadPrefs.RunTabEventsTimelineFoldout = !HotReloadPrefs.RunTabEventsTimelineFoldout; + } + if (HotReloadPrefs.RunTabEventsTimelineFoldout) { + GUILayout.Space(-10); + var noteShown = HotReloadTimelineHelper.GetRunTabTimelineEventCount() == 0 || !currentState.running; + using (new EditorGUILayout.HorizontalScope()) { + if (noteShown) { + GUILayout.Space(2f); + using (new EditorGUILayout.VerticalScope()) { + GUILayout.Space(2f); + string text; + if (currentState.redeemStage != RedeemStage.None) { + text = Translations.Timeline.MessageCompleteRegistration; + } else if (!currentState.running) { + text = Translations.Timeline.MessageUseStartButton; + } else if (enabledFilters.Count < 4 && HotReloadTimelineHelper.EventsTimeline.Count != 0) { + text = Translations.Timeline.MessageEnableFilters; + } else { + text = Translations.Timeline.MessageMakeCodeChanges; + } + GUILayout.Label(text, HotReloadWindowStyles.EmptyListText); + } + GUILayout.FlexibleSpace(); + } else { + GUILayout.FlexibleSpace(); + if (HotReloadTimelineHelper.EventsTimeline.Count > 0 && GUILayout.Button(Translations.Common.ButtonClear)) { + HotReloadTimelineHelper.ClearEntries(); + if (HotReloadWindow.Current) { + HotReloadWindow.Current.Repaint(); + } + } + GUILayout.Space(3); + } + } + if (!noteShown) { + GUILayout.Space(2f); + using (new EditorGUILayout.VerticalScope()) { + RenderEntries(TimelineType.Timeline); + } + } + } + } + } + } + + internal static void RenderConsumption(LoginStatusResponse loginStatus) { + if (loginStatus == null) { + return; + } + EditorGUILayout.Space(); + + EditorGUILayout.LabelField(Translations.License.TitleHotReloadLimited, HotReloadWindowStyles.H3CenteredTitleStyle); + EditorGUILayout.Space(); + if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.NetworkUnreachable) { + EditorGUILayout.HelpBox(Translations.Errors.ErrorNetworkIssue, MessageType.Warning); + } else if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.UnrecoverableError) { + EditorGUILayout.HelpBox(Translations.Errors.ErrorContactSupport, MessageType.Error); + } else if (loginStatus.freeSessionFinished) { + var now = DateTime.UtcNow; + var sessionRefreshesAt = (now.AddDays(1).Date - now).Add(TimeSpan.FromMinutes(5)); + var sessionRefreshString = sessionRefreshesAt.Hours > 0 ? + string.Format(Translations.Miscellaneous.DailySessionNextSessionHours, sessionRefreshesAt.Hours, sessionRefreshesAt.Minutes) : + string.Format(Translations.Miscellaneous.DailySessionNextSessionMinutes, sessionRefreshesAt.Minutes); + HotReloadGUIHelper.HelpBox(sessionRefreshString, MessageType.Warning, fontSize: 11); + } else if (loginStatus.freeSessionRunning && loginStatus.freeSessionEndTime != null) { + var sessionEndsAt = loginStatus.freeSessionEndTime.Value - DateTime.Now; + var sessionString = sessionEndsAt.Hours > 0 ? + string.Format(Translations.Miscellaneous.DailySessionTimeHoursLeft, sessionEndsAt.Hours, sessionEndsAt.Minutes) : + string.Format(Translations.Miscellaneous.DailySessionTimeMinutesLeft, sessionEndsAt.Minutes); + HotReloadGUIHelper.HelpBox(sessionString, MessageType.Info, fontSize: 11); + } else if (loginStatus.freeSessionEndTime == null) { + HotReloadGUIHelper.HelpBox(Translations.Miscellaneous.DailySessionStart, MessageType.Info, fontSize: 11); + } + } + + static bool _repaint; + static bool _instantRepaint; + static DateTime _lastRepaint; + private EditorIndicationState.IndicationStatus _lastStatus; + public override void Update() { + if (EditorIndicationState.SpinnerActive) { + _repaint = true; + } + if (EditorCodePatcher.DownloadRequired) { + _repaint = true; + } + if (EditorIndicationState.IndicationIconPath == Spinner.SpinnerIconPath) { + _repaint = true; + } + try { + // workaround: hovering over non-buttons doesn't repain by default + if (EditorWindow.mouseOverWindow == HotReloadWindow.Current) { + _repaint = true; + } + if (EditorWindow.mouseOverWindow + && EditorWindow.mouseOverWindow?.GetType() == typeof(PopupWindow) + && HotReloadEventPopup.I.open + ) { + _repaint = true; + } + } catch (NullReferenceException) { + // Unity randomly throws nullrefs when EditorWindow.mouseOverWindow gets accessed + } + if (_repaint && DateTime.UtcNow - _lastRepaint > TimeSpan.FromMilliseconds(33)) { + _repaint = false; + _instantRepaint = true; + } + // repaint on status change + var status = EditorIndicationState.CurrentIndicationStatus; + if (_lastStatus != status) { + _lastStatus = status; + _instantRepaint = true; + } + if (_instantRepaint) { + Repaint(); + HotReloadEventPopup.I.Repaint(); + _instantRepaint = false; + _repaint = false; + _lastRepaint = DateTime.UtcNow; + } + } + + public static void RepaintInstant() { + _instantRepaint = true; + } + + private void RenderRecompileButton() { + string recompileText = HotReloadWindowStyles.windowScreenWidth > Constants.RecompileButtonTextHideWidth ? Translations.UI.RecompileButtonLabel : ""; + var recompileButton = new GUIContent(recompileText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Recompile)); + if (!GUILayout.Button(recompileButton, HotReloadWindowStyles.RecompileButton)) { + return; + } + RecompileWithChecks(); + } + + public static void RecompileWithChecks() { + var firstDialoguePass = HotReloadPrefs.RecompileDialogueShown + || EditorUtility.DisplayDialog( + title: Translations.Dialogs.DialogTitleRecompile, + message: Translations.Dialogs.DialogMessageRecompile, + ok: Translations.Common.ButtonRecompile.Trim(), + cancel: Translations.Common.ButtonNotNow); + HotReloadPrefs.RecompileDialogueShown = true; + if (!firstDialoguePass) { + return; + } + if (!ConfirmExitPlaymode(Translations.Dialogs.DialogMessageStopPlayMode)) { + return; + } + Recompile(); + } + + #if UNITY_2020_1_OR_NEWER + public static void SwitchToDebugMode() { + CompilationPipeline.codeOptimization = CodeOptimization.Debug; + HotReloadRunTab.Recompile(); + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods); + } + #endif + + public static bool ConfirmExitPlaymode(string message) { + return !Application.isPlaying + || EditorUtility.DisplayDialog( + title: Translations.Dialogs.DialogTitleStopPlayMode, + message: message, + ok: Translations.Common.ButtonStopAndRecompile, + cancel: Translations.Common.ButtonCancel); + } + + public static bool recompiling; + public static void Recompile() { + recompiling = true; + EditorApplication.isPlaying = false; + + CompileMethodDetourer.Reset(); + AssetDatabase.Refresh(); + // This forces the recompilation if no changes were made. + // This is better UX because otherwise the recompile button is unresponsive + // which can be extra annoying if there are compile error entries in the list + if (!EditorApplication.isCompiling) { + CompilationPipeline.RequestScriptCompilation(); + } + } + + private void RenderIndicationButtons() { + if (currentState.requestingDownloadAndRun || currentState.starting || currentState.stopping || currentState.redeemStage != RedeemStage.None) { + return; + } + + if (!currentState.running && (currentState.startupProgress?.Item1 ?? 0) == 0) { + string startText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? Translations.UI.StartButtonLabel : ""; + if (GUILayout.Button(new GUIContent(startText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Start)), HotReloadWindowStyles.StartButton)) { + EditorCodePatcher.DownloadAndRun().Forget(); + } + } else if (currentState.running && !currentState.starting) { + if (HotReloadWindowStyles.windowScreenWidth > 150) { + RenderRecompileButton(); + } + string stopText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? Translations.UI.StopButtonLabel : ""; + if (GUILayout.Button(new GUIContent(stopText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Stop)), HotReloadWindowStyles.StopButton)) { + if (!EditorCodePatcher.StoppedServerRecently()) { + EditorCodePatcher.StopCodePatcher().Forget(); + } + } + } + } + + void RenderIndicationPanel(bool renderLicenseInfo = true) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBox)) { + RenderIndication(); + if (HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + RenderIndicationButtons(); + if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + } + if (currentState.requestingDownloadAndRun || currentState.starting) { + RenderProgressBar(); + if (EditorCodePatcher.serverDownloader.Attempts > 0) { + RenderManualDownloadSection(); + } + } + + if (HotReloadWindowStyles.windowScreenWidth > Constants.ConsumptionsHideWidth + && HotReloadWindowStyles.windowScreenHeight > Constants.ConsumptionsHideHeight + && renderLicenseInfo + ) { + RenderLicenseInfo(currentState); + } + } + + bool copiedPath = false; + bool openedDownloadUrl = false; + void RenderManualDownloadSection() { + var downloadUrl = ServerDownloader.GetDownloadUrl(HotReloadCli.controller); + var downloadPath = EditorCodePatcher.serverDownloader.GetBinaryPath(HotReloadCli.controller); + + EditorGUILayout.Space(); + HotReloadGUIHelper.HelpBox( + Translations.Timeline.ManualDownloadWarning, + MessageType.Warning, 11); + + HotReloadGUIHelper.HelpBox( + string.Format(Translations.Timeline.ManualDownloadInfo, downloadPath), + MessageType.Info, 11); + + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(Translations.Timeline.ManualDownloadButtonCopyToClipboard + (copiedPath ? " ✓" : ""))) { + GUIUtility.systemCopyBuffer = downloadPath; + copiedPath = true; + } + if (GUILayout.Button(Translations.Timeline.ManualDownloadButtonOpenDownloadUrl + (openedDownloadUrl ? " ✓" : ""))) { + Application.OpenURL(downloadUrl); + openedDownloadUrl = true; + } + } + if (GUILayout.Button(Translations.Timeline.ManualDownloadButtonComplete)) { + EditorCodePatcher.downloadCancelToken?.Cancel(); + copiedPath = false; + openedDownloadUrl = false; + } + OpenURLButton.Render(Translations.Timeline.ManualDownloadButtonContactSupport, Constants.ContactURL); + EditorGUILayout.Space(); + } + + internal static void RenderLicenseInfo(HotReloadRunTabState currentState) { + var showRedeem = currentState.redeemStage != RedeemStage.None; + var showConsumptions = ShouldRenderConsumption(currentState); + if (!showConsumptions && !showRedeem) { + return; + } + using (new EditorGUILayout.VerticalScope()) { + // space needed only for consumptions because of Stop/Start button's margin + if (showConsumptions) { + GUILayout.Space(6); + } + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Section)) { + if (showRedeem) { + RedeemLicenseHelper.I.RenderStage(currentState); + } else { + RenderConsumption(currentState.loginStatus); + GUILayout.Space(10); + RenderLicenseInfo(currentState, currentState.loginStatus); + RenderLicenseButtons(currentState); + GUILayout.Space(10); + } + } + GUILayout.Space(6); + } + } + + private Spinner _spinner = new Spinner(85); + private void RenderIndication() { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationBox)) { + // icon box + if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationHelpBox)) { + var text = HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth ? $" {currentState.indicationStatusText}" : ""; + if (currentState.indicationIconPath == Spinner.SpinnerIconPath) { + GUILayout.Label(new GUIContent(text, _spinner.GetIcon()), style: HotReloadWindowStyles.IndicationIcon); + } else if (currentState.indicationIconPath != null) { + var style = HotReloadWindowStyles.IndicationIcon; + if (HotReloadTimelineHelper.alertIconString.ContainsValue(currentState.indicationIconPath)) { + style = HotReloadWindowStyles.IndicationAlertIcon; + } + GUILayout.Label(new GUIContent(text, GUIHelper.GetLocalIcon(currentState.indicationIconPath)), style); + } + } + } + } + + static GUIStyle _openSettingsStyle; + static GUIStyle openSettingsStyle => _openSettingsStyle ?? (_openSettingsStyle = new GUIStyle(GUI.skin.button) { + fontStyle = FontStyle.Normal, + fixedHeight = 25, + }); + + static GUILayoutOption[] _bigButtonHeight; + public static GUILayoutOption[] bigButtonHeight => _bigButtonHeight ?? (_bigButtonHeight = new [] {GUILayout.Height(25)}); + + private static GUIContent indieLicenseContent; + private static GUIContent businessLicenseContent; + + internal static void RenderLicenseStatusInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool allowHide = true, bool verbose = false) { + string message = null; + MessageType messageType = default(MessageType); + Action customGUI = null; + GUIContent content = null; + if (loginStatus == null) { + // no info + } else if (loginStatus.lastLicenseError != null) { + messageType = !loginStatus.freeSessionFinished ? MessageType.Warning : MessageType.Error; + message = GetMessageFromError(currentState, loginStatus.lastLicenseError); + } else if (loginStatus.isTrial && !PackageConst.IsAssetStoreBuild) { + message = string.Format(Translations.UI.TrialLicenseMessage, loginStatus.licenseExpiresAt.ToShortDateString()); + messageType = MessageType.Info; + } else if (loginStatus.isIndieLicense) { + if (verbose) { + message = Translations.UI.IndieLicenseMessage; + messageType = MessageType.Info; + customGUI = () => { + if (loginStatus.licenseExpiresAt.Date != DateTime.MaxValue.Date) { + EditorGUILayout.LabelField(string.Format(Translations.UI.LicenseRenewalMessage, loginStatus.licenseExpiresAt.ToShortDateString())); + EditorGUILayout.Space(); + } + using (new GUILayout.HorizontalScope()) { + HotReloadAboutTab.manageLicenseButton.OnGUI(); + HotReloadAboutTab.manageAccountButton.OnGUI(); + } + EditorGUILayout.Space(); + }; + if (indieLicenseContent == null) { + indieLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed")); + } + content = indieLicenseContent; + } + } else if (loginStatus.isBusinessLicense) { + if (verbose) { + message = Translations.UI.BusinessLicenseMessage; + messageType = MessageType.Info; + if (businessLicenseContent == null) { + businessLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed")); + } + content = businessLicenseContent; + customGUI = () => { + using (new GUILayout.HorizontalScope()) { + HotReloadAboutTab.manageLicenseButton.OnGUI(); + HotReloadAboutTab.manageAccountButton.OnGUI(); + } + EditorGUILayout.Space(); + }; + } + } + + if (messageType != MessageType.Info && HotReloadPrefs.ErrorHidden && allowHide) { + return; + } + if (message != null) { + if (messageType != MessageType.Info) { + using(new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(message, messageType); + var style = HotReloadWindowStyles.HideButtonStyle; + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (allowHide) { + if (GUILayout.Button(Translations.Common.ButtonHide, style)) { + HotReloadPrefs.ErrorHidden = true; + } + } + } + } else if (content != null) { + EditorGUILayout.LabelField(content); + EditorGUILayout.Space(); + } else { + EditorGUILayout.LabelField(message); + EditorGUILayout.Space(); + } + customGUI?.Invoke(); + } + } + + internal static void RenderBusinessLicenseInfo(GUIStyle style) { + GUILayout.Space(8); + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(Translations.License.LicenseErrorAssetStorePro, MessageType.Info); + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (GUILayout.Button(Translations.Common.ButtonUpgrade, style)) { + Application.OpenURL(Constants.ProductPurchaseBusinessURL); + } + } + } + + internal static void RenderDebuggerAttachedInfo(bool isPopup) { + GUILayout.Space(8); + var autoRefreshDisabled = AutoRefreshSettingChecker.IsUserAutoRefreshDisabled(); + var msg = autoRefreshDisabled ? Translations.Suggestions.DebuggerAttachedMessagePaused : Translations.Suggestions.DebuggerAttachedMessageAutoRecompile; + using (new EditorGUILayout.VerticalScope()) { + var _fontSize = EditorStyles.helpBox.fontSize; + try { + EditorStyles.helpBox.fontSize = 12; + // empty label field to measure width + EditorGUILayout.LabelField(GUIContent.none, new GUILayoutOption[]{GUILayout.Height(0)}); + var lastRectWidth = GUILayoutUtility.GetLastRect().width; + EditorStyles.helpBox.fixedHeight = EditorStyles.helpBox.CalcHeight(new GUIContent(msg), lastRectWidth) + 15; + EditorStyles.helpBox.fixedWidth = lastRectWidth; + EditorGUILayout.HelpBox(msg, MessageType.Info); + // to account for added height + GUILayout.Space(isPopup ? 45 : 30); + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button("Docs")) { + Application.OpenURL(Constants.DebuggerURL); + } + if (GUILayout.Button("Open Settings") && HotReloadWindow.Current) { + HotReloadWindow.Current.SelectTab(typeof(HotReloadSettingsTab)); + } + } + } finally { + EditorStyles.helpBox.fixedHeight = 0; + EditorStyles.helpBox.fixedWidth = 0; + EditorStyles.helpBox.fontSize = _fontSize; + } + } + } + + internal static void RenderIndieLicenseInfo(GUIStyle style) { + GUILayout.Space(8); + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(Translations.License.LicenseErrorUnityPlusIndie, MessageType.Info); + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (GUILayout.Button(Translations.Common.ButtonUpgrade, style)) { + Application.OpenURL(Constants.ProductPurchaseURL); + } + } + } + + internal static Dictionary _licenseErrorData; + internal static Dictionary LicenseErrorData => _licenseErrorData ?? (_licenseErrorData = new Dictionary { + { "DeviceNotLicensedException", new LicenseErrorData(description: Translations.License.LicenseErrorDeviceInUse, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport) }, + { "DeviceBlacklistedException", new LicenseErrorData(description: Translations.License.LicenseErrorDeviceBlacklisted) }, + { "DateHeaderInvalidException", new LicenseErrorData(description: Translations.License.LicenseErrorIncorrectClock) }, + { "DateTimeCheatingException", new LicenseErrorData(description: Translations.License.LicenseErrorIncorrectClock) }, + { "LicenseActivationException", new LicenseErrorData(description: Translations.License.LicenseErrorActivation, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport) }, + { "LicenseDeletedException", new LicenseErrorData(description: Translations.License.LicenseErrorDeleted, showBuyButton: true, buyButtonText: Translations.License.LicenseButtonGetLicense, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport) }, + { "LicenseDisabledException", new LicenseErrorData(description: Translations.License.LicenseErrorDisabled, showBuyButton: true, buyButtonText: Translations.License.LicenseButtonGetLicense, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport) }, + { "LicenseExpiredException", new LicenseErrorData(description: Translations.License.LicenseErrorExpired, showBuyButton: true, buyButtonText: Translations.License.LicenseButtonUpgradeLicense, showManageLicenseButton: true, manageLicenseButtonText: Translations.License.LicenseButtonManageLicense) }, + { "LicenseInactiveException", new LicenseErrorData(description: Translations.License.LicenseErrorInactive) }, + { "LocalLicenseException", new LicenseErrorData(description: Translations.License.LicenseErrorCorrupted) }, + // Note: obsolete + { "MissingParametersException", new LicenseErrorData(description: "An account already exists for this device. Please login with your existing email/password.", showBuyButton: true, buyButtonText: Translations.License.LicenseButtonGetLicense) }, + { "NetworkException", new LicenseErrorData(description: Translations.License.LicenseErrorNetwork, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport) }, + { "TrialLicenseExpiredException", new LicenseErrorData(description: Translations.License.LicenseErrorTrialExpired, showBuyButton: true, buyButtonText: Translations.License.LicenseButtonUpgradeLicense) }, + { "InvalidCredentialException", new LicenseErrorData(description: Translations.License.LicenseErrorInvalidCredentials) }, + // Note: activating free trial with email is not supported anymore. This error shouldn't happen which is why we should rather user the fallback + // { "LicenseNotFoundException", new LicenseErrorData(description: "The account you're trying to access doesn't seem to exist yet. Please enter your email address to create a new account and receive a trial license.", showLoginButton: true, loginButtonText: CreateAccount) }, + { "LicenseIncompatibleException", new LicenseErrorData(description: Translations.License.LicenseErrorIncompatible, showManageLicenseButton: true, manageLicenseButtonText: Translations.License.LicenseButtonManageLicense) }, + }); + internal static LicenseErrorData defaultLicenseErrorData = new LicenseErrorData(description: Translations.License.LicenseErrorDefault, showSupportButton: true, supportButtonText: Translations.License.LicenseButtonContactSupport); + + internal static string GetMessageFromError(HotReloadRunTabState currentState, string error) { + if (PackageConst.IsAssetStoreBuild && error == "TrialLicenseExpiredException") { + return Translations.License.LicenseErrorAssetStorePro; + } + return GetLicenseErrorDataOrDefault(currentState, error).description; + } + + internal static LicenseErrorData GetLicenseErrorDataOrDefault(HotReloadRunTabState currentState, string error) { + if (currentState.loginStatus?.isFree == true) { + return default(LicenseErrorData); + } + if (currentState.loginStatus == null || string.IsNullOrEmpty(error) && (!currentState.loginStatus.isLicensed || currentState.loginStatus.isTrial)) { + return new LicenseErrorData(null, showBuyButton: true, buyButtonText: Translations.License.LicenseButtonGetLicense); + } + if (string.IsNullOrEmpty(error)) { + return default(LicenseErrorData); + } + if (!LicenseErrorData.ContainsKey(error)) { + return defaultLicenseErrorData; + } + return LicenseErrorData[error]; + } + + internal static void RenderBuyLicenseButton(string buyLicenseButton) { + OpenURLButton.Render(buyLicenseButton, Constants.ProductPurchaseURL); + } + + static void RenderLicenseActionButtons(HotReloadRunTabState currentState) { + var errInfo = GetLicenseErrorDataOrDefault(currentState, currentState.loginStatus?.lastLicenseError); + if (errInfo.showBuyButton || errInfo.showManageLicenseButton) { + using(new EditorGUILayout.HorizontalScope()) { + if (errInfo.showBuyButton) { + RenderBuyLicenseButton(errInfo.buyButtonText); + } + if (errInfo.showManageLicenseButton && !HotReloadPrefs.ErrorHidden) { + OpenURLButton.Render(errInfo.manageLicenseButtonText, Constants.ManageLicenseURL); + } + } + } + if (errInfo.showLoginButton && GUILayout.Button(errInfo.loginButtonText, openSettingsStyle)) { + // show license section + HotReloadWindow.Current.SelectTab(typeof(HotReloadSettingsTab)); + HotReloadWindow.Current.SettingsTab.FocusLicenseFoldout(); + } + if (errInfo.showSupportButton && !HotReloadPrefs.ErrorHidden) { + OpenURLButton.Render(errInfo.supportButtonText, Constants.ContactURL); + } + if (currentState.loginStatus?.lastLicenseError != null) { + HotReloadAboutTab.reportIssueButton.OnGUI(); + } + } + + internal static void RenderLicenseInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool verbose = false, bool allowHide = true, string overrideActionButton = null, bool showConsumptions = false) { + HotReloadPrefs.ShowLogin = EditorGUILayout.Foldout(HotReloadPrefs.ShowLogin, Translations.License.TitleHotReloadLicense, true, HotReloadWindowStyles.FoldoutStyle); + if (HotReloadPrefs.ShowLogin) { + EditorGUILayout.Space(); + if ((loginStatus?.isLicensed != true && showConsumptions) && !(loginStatus == null || loginStatus.isFree)) { + RenderConsumption(loginStatus); + } + RenderLicenseStatusInfo(currentState, loginStatus: loginStatus, allowHide: allowHide, verbose: verbose); + + RenderLicenseInnerPanel(currentState, overrideActionButton: overrideActionButton); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + } + } + + internal void RenderPromoCodes() { + HotReloadPrefs.ShowPromoCodes = EditorGUILayout.Foldout(HotReloadPrefs.ShowPromoCodes, Translations.License.PromoCodesTitle, true, HotReloadWindowStyles.FoldoutStyle); + if (!HotReloadPrefs.ShowPromoCodes) { + return; + } + if (promoCodeActivatedThisSession) { + EditorGUILayout.HelpBox(Translations.License.MessagePromoCodeActivated, MessageType.Info); + } else { + if (promoCodeError != null && promoCodeErrorType != MessageType.None) { + EditorGUILayout.HelpBox(promoCodeError, promoCodeErrorType); + } + EditorGUILayout.LabelField(Translations.Common.LabelPromoCode); + _pendingPromoCode = EditorGUILayout.TextField(_pendingPromoCode); + EditorGUILayout.Space(); + + using (new EditorGUI.DisabledScope(_requestingActivatePromoCode)) { + if (GUILayout.Button(Translations.Common.ButtonActivatePromoCode, HotReloadRunTab.bigButtonHeight)) { + RequestActivatePromoCode().Forget(); + } + } + } + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + } + + private async Task RequestActivatePromoCode() { + _requestingActivatePromoCode = true; + try { + var resp = await RequestHelper.RequestActivatePromoCode(_pendingPromoCode); + if (resp != null && resp.error == null) { + promoCodeActivatedThisSession = true; + } else { + var requestError = resp?.error ?? "Network error"; + var errorType = ToErrorType(requestError); + promoCodeError = ToPrettyErrorMessage(errorType); + promoCodeErrorType = ToMessageType(errorType); + } + } finally { + _requestingActivatePromoCode = false; + } + } + + PromoCodeErrorType ToErrorType(string error) { + switch (error) { + case "Input is missing": return PromoCodeErrorType.MISSING_INPUT; + case "only POST is supported": return PromoCodeErrorType.INVALID_HTTP_METHOD; + case "body is not a valid json": return PromoCodeErrorType.BODY_INVALID; + case "Promo code is not found": return PromoCodeErrorType.PROMO_CODE_NOT_FOUND; + case "Promo code already claimed": return PromoCodeErrorType.PROMO_CODE_CLAIMED; + case "Promo code expired": return PromoCodeErrorType.PROMO_CODE_EXPIRED; + case "License not found": return PromoCodeErrorType.LICENSE_NOT_FOUND; + case "License is not a trial": return PromoCodeErrorType.LICENSE_NOT_TRIAL; + case "License already extended": return PromoCodeErrorType.LICENSE_ALREADY_EXTENDED; + case "conditionalCheckFailed": return PromoCodeErrorType.CONDITIONAL_CHECK_FAILED; + } + if (error.Contains("Updating License Failed with error")) { + return PromoCodeErrorType.UPDATING_LICENSE_FAILED; + } else if (error.Contains("Unknown exception")) { + return PromoCodeErrorType.UNKNOWN_EXCEPTION; + } else if (error.Contains("Unsupported path")) { + return PromoCodeErrorType.UNSUPPORTED_PATH; + } + return PromoCodeErrorType.NONE; + } + + string ToPrettyErrorMessage(PromoCodeErrorType errorType) { + var defaultMsg = Translations.Errors.ErrorPromoCodeActivation; + switch (errorType) { + case PromoCodeErrorType.MISSING_INPUT: + case PromoCodeErrorType.INVALID_HTTP_METHOD: + case PromoCodeErrorType.BODY_INVALID: + case PromoCodeErrorType.UNKNOWN_EXCEPTION: + case PromoCodeErrorType.UNSUPPORTED_PATH: + case PromoCodeErrorType.LICENSE_NOT_FOUND: + case PromoCodeErrorType.UPDATING_LICENSE_FAILED: + case PromoCodeErrorType.LICENSE_NOT_TRIAL: + return defaultMsg; + case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return Translations.Errors.ErrorPromoCodeInvalid; + case PromoCodeErrorType.PROMO_CODE_CLAIMED: return Translations.Errors.ErrorPromoCodeUsed; + case PromoCodeErrorType.PROMO_CODE_EXPIRED: return Translations.Errors.ErrorPromoCodeExpired; + case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return Translations.Errors.ErrorLicenseExtended; + case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return Translations.Errors.ErrorPromoCodeActivation; + case PromoCodeErrorType.NONE: return Translations.Errors.ErrorPromoCodeNetwork; + default: return defaultMsg; + } + } + + MessageType ToMessageType(PromoCodeErrorType errorType) { + switch (errorType) { + case PromoCodeErrorType.MISSING_INPUT: return MessageType.Error; + case PromoCodeErrorType.INVALID_HTTP_METHOD: return MessageType.Error; + case PromoCodeErrorType.BODY_INVALID: return MessageType.Error; + case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return MessageType.Warning; + case PromoCodeErrorType.PROMO_CODE_CLAIMED: return MessageType.Warning; + case PromoCodeErrorType.PROMO_CODE_EXPIRED: return MessageType.Warning; + case PromoCodeErrorType.LICENSE_NOT_FOUND: return MessageType.Error; + case PromoCodeErrorType.LICENSE_NOT_TRIAL: return MessageType.Error; + case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return MessageType.Warning; + case PromoCodeErrorType.UPDATING_LICENSE_FAILED: return MessageType.Error; + case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return MessageType.Error; + case PromoCodeErrorType.UNKNOWN_EXCEPTION: return MessageType.Error; + case PromoCodeErrorType.UNSUPPORTED_PATH: return MessageType.Error; + case PromoCodeErrorType.NONE: return MessageType.Error; + default: return MessageType.Error; + } + } + + public static void RenderLicenseButtons(HotReloadRunTabState currentState) { + RenderLicenseActionButtons(currentState); + } + + internal static void RenderLicenseInnerPanel(HotReloadRunTabState currentState, string overrideActionButton = null, bool renderLogout = true) { + EditorGUILayout.LabelField(Translations.Common.LabelEmail); + GUI.SetNextControlName("email"); + _pendingEmail = EditorGUILayout.TextField(string.IsNullOrEmpty(_pendingEmail) ? HotReloadPrefs.LicenseEmail : _pendingEmail); + _pendingEmail = _pendingEmail.Trim(); + + EditorGUILayout.LabelField(Translations.Common.LabelPassword); + GUI.SetNextControlName("password"); + _pendingPassword = EditorGUILayout.PasswordField(string.IsNullOrEmpty(_pendingPassword) ? HotReloadPrefs.LicensePassword : _pendingPassword); + + RenderSwitchAuthMode(); + + var e = Event.current; + using(new EditorGUI.DisabledScope(currentState.requestingLoginInfo)) { + var btnLabel = overrideActionButton; + if (String.IsNullOrEmpty(overrideActionButton)) { + btnLabel = Translations.Common.ButtonLogin; + } + using (new EditorGUILayout.HorizontalScope()) { + var focusedControl = GUI.GetNameOfFocusedControl(); + if (GUILayout.Button(btnLabel, bigButtonHeight) + || (focusedControl == "email" + || focusedControl == "password") + && e.type == EventType.KeyUp + && (e.keyCode == KeyCode.Return + || e.keyCode == KeyCode.KeypadEnter) + ) { + var error = ValidateEmail(_pendingEmail); + if (!string.IsNullOrEmpty(error)) { + _activateInfoMessage = new Tuple(error, MessageType.Warning); + } else if (string.IsNullOrEmpty(_pendingPassword)) { + _activateInfoMessage = new Tuple(Translations.Errors.ErrorEnterPassword, MessageType.Warning); + } else { + HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab)); + + _activateInfoMessage = null; + if (RedeemLicenseHelper.I.RedeemStage == RedeemStage.Login) { + RedeemLicenseHelper.I.FinishRegistration(RegistrationOutcome.Indie); + } + if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) { + LoginOnDownloadAndRun(new LoginData(email: _pendingEmail, password: _pendingPassword)).Forget(); + } else { + EditorCodePatcher.RequestLogin(_pendingEmail, _pendingPassword).Forget(); + } + } + } + if (renderLogout) { + RenderLogout(currentState); + } + } + } + if (_activateInfoMessage != null && (e.type == EventType.Layout || e.type == EventType.Repaint)) { + EditorGUILayout.HelpBox(_activateInfoMessage.Item1, _activateInfoMessage.Item2); + } + } + + public static string ValidateEmail(string email) { + if (string.IsNullOrEmpty(email)) { + return Translations.Errors.ErrorEnterEmail; + } else if (!EditorWindowHelper.IsValidEmailAddress(email)) { + return Translations.Errors.ErrorValidEmail; + } else if (email.Contains("+")) { + return Translations.Errors.ErrorMailExtensions; + } + return null; + } + + public static void RenderLogout(HotReloadRunTabState currentState) { + if (currentState.loginStatus?.isLicensed != true) { + return; + } + if (GUILayout.Button(Translations.Common.ButtonLogout, bigButtonHeight)) { + HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab)); + if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) { + LogoutOnDownloadAndRun().Forget(); + } else { + RequestLogout().Forget(); + } + } + } + + async static Task LoginOnDownloadAndRun(LoginData loginData = null) { + var ok = await EditorCodePatcher.DownloadAndRun(loginData); + if (ok && loginData != null) { + HotReloadPrefs.ErrorHidden = false; + HotReloadPrefs.LicenseEmail = loginData.email; + HotReloadPrefs.LicensePassword = loginData.password; + } + } + + async static Task LogoutOnDownloadAndRun() { + var ok = await EditorCodePatcher.DownloadAndRun(); + if (!ok) { + return; + } + await RequestLogout(); + } + + private async static Task RequestLogout() { + int i = 0; + while (!EditorCodePatcher.Running && i < 100) { + await Task.Delay(100); + i++; + } + var resp = await RequestHelper.RequestLogout(); + if (!EditorCodePatcher.RequestingLoginInfo && resp != null) { + EditorCodePatcher.HandleStatus(resp); + } + } + + private static void RenderSwitchAuthMode() { + var color = EditorGUIUtility.isProSkin ? new Color32(0x3F, 0x9F, 0xFF, 0xFF) : new Color32(0x0F, 0x52, 0xD7, 0xFF); + if (HotReloadGUIHelper.LinkLabel(Translations.Miscellaneous.LinkForgotPassword, 12, FontStyle.Normal, TextAnchor.MiddleLeft, color)) { + if (EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRecoverPassword, Translations.Dialogs.DialogMessageRecoverPassword, Translations.Common.ButtonOpenInBrowser, Translations.Common.ButtonCancel)) { + Application.OpenURL(Constants.ForgotPasswordURL); + } + } + } + + Texture2D _greenTextureLight; + Texture2D _greenTextureDark; + Texture2D GreenTexture => EditorGUIUtility.isProSkin + ? _greenTextureDark ? _greenTextureDark : (_greenTextureDark = MakeTexture(0.5f)) + : _greenTextureLight ? _greenTextureLight : (_greenTextureLight = MakeTexture(0.85f)); + + private void RenderProgressBar() { + if (currentState.downloadRequired && !currentState.downloadStarted) { + return; + } + + using(var scope = new EditorGUILayout.VerticalScope(HotReloadWindowStyles.MiddleCenterStyle)) { + float progress; + var bg = HotReloadWindowStyles.ProgressBarBarStyle.normal.background; + try { + HotReloadWindowStyles.ProgressBarBarStyle.normal.background = GreenTexture; + var barRect = scope.rect; + + barRect.height = 25; + if (currentState.downloadRequired) { + barRect.width = barRect.width - 65; + using (new EditorGUILayout.HorizontalScope()) { + progress = EditorCodePatcher.DownloadProgress; + EditorGUI.ProgressBar(barRect, Mathf.Clamp(progress, 0f, 1f), ""); + if (GUI.Button(new Rect(barRect) { x = barRect.x + barRect.width + 5, height = barRect.height, width = 60 }, new GUIContent(" Info", GUIHelper.GetLocalIcon("alert_info")))) { + Application.OpenURL(Constants.AdditionalContentURL); + } + } + } else { + progress = EditorCodePatcher.Stopping ? 1 : Mathf.Clamp(EditorCodePatcher.StartupProgress?.Item1 ?? 0f, 0f, 1f); + EditorGUI.ProgressBar(barRect, progress, ""); + } + GUILayout.Space(barRect.height); + } finally { + HotReloadWindowStyles.ProgressBarBarStyle.normal.background = bg; + } + } + } + + private Texture2D MakeTexture(float maxHue) { + var width = 11; + var height = 11; + Color[] pix = new Color[width * height]; + for (int y = 0; y < height; y++) { + var middle = Math.Ceiling(height / (double)2); + var maxGreen = maxHue; + var yCoord = y + 1; + var green = maxGreen - Math.Abs(yCoord - middle) * 0.02; + for (int x = 0; x < width; x++) { + pix[y * width + x] = new Color(0.1f, (float)green, 0.1f, 1.0f); + } + } + var result = new Texture2D(width, height); + result.SetPixels(pix); + result.Apply(); + return result; + } + + + /* + [MenuItem("codepatcher/restart")] + public static void TestRestart() { + CodePatcherCLI.Restart(Application.dataPath, false); + } + */ + + } + + internal static class HotReloadGUIHelper { + public static bool LinkLabel(string labelText, int fontSize, FontStyle fontStyle, TextAnchor alignment, Color? color = null) { + var stl = EditorStyles.label; + + // copy + var origSize = stl.fontSize; + var origStyle = stl.fontStyle; + var origAnchor = stl.alignment; + var origColor = stl.normal.textColor; + + // temporarily modify the built-in style + stl.fontSize = fontSize; + stl.fontStyle = fontStyle; + stl.alignment = alignment; + stl.normal.textColor = color ?? origColor; + stl.active.textColor = color ?? origColor; + stl.focused.textColor = color ?? origColor; + stl.hover.textColor = color ?? origColor; + + try { + return GUILayout.Button(labelText, stl); + } finally{ + // set the editor style (stl) back to normal + stl.fontSize = origSize; + stl.fontStyle = origStyle; + stl.alignment = origAnchor; + stl.normal.textColor = origColor; + stl.active.textColor = origColor; + stl.focused.textColor = origColor; + stl.hover.textColor = origColor; + } + } + + public static void HelpBox(string message, MessageType type, int fontSize) { + var _fontSize = EditorStyles.helpBox.fontSize; + try { + EditorStyles.helpBox.fontSize = fontSize; + EditorGUILayout.HelpBox(message, type); + } finally { + EditorStyles.helpBox.fontSize = _fontSize; + } + } + } + + internal enum PromoCodeErrorType { + NONE, + MISSING_INPUT, + INVALID_HTTP_METHOD, + BODY_INVALID, + PROMO_CODE_NOT_FOUND, + PROMO_CODE_CLAIMED, + PROMO_CODE_EXPIRED, + LICENSE_NOT_FOUND, + LICENSE_NOT_TRIAL, + LICENSE_ALREADY_EXTENDED, + UPDATING_LICENSE_FAILED, + CONDITIONAL_CHECK_FAILED, + UNKNOWN_EXCEPTION, + UNSUPPORTED_PATH, + } + + internal class LoginData { + public readonly string email; + public readonly string password; + + public LoginData(string email, string password) { + this.email = email; + this.password = password; + } + } +} + diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs.meta new file mode 100644 index 0000000..22d43bf --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 38d0877009d34a9458f7d169d7f1b6a7 +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/Window/GUI/Tabs/HotReloadRunTab.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs new file mode 100644 index 0000000..3589f3f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs @@ -0,0 +1,945 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.Editor.Localization; +using RuntimeLocalization = SingularityGroup.HotReload.Localization; +using UnityEditor; +using UnityEngine; +using EditorGUI = UnityEditor.EditorGUI; + +namespace SingularityGroup.HotReload.Editor { + internal struct HotReloadSettingsTabState { + public readonly bool running; + public readonly bool trialLicense; + public readonly LoginStatusResponse loginStatus; + public readonly bool isServerHealthy; + public readonly bool registrationRequired; + + public HotReloadSettingsTabState( + bool running, + bool trialLicense, + LoginStatusResponse loginStatus, + bool isServerHealthy, + bool registrationRequired + ) { + this.running = running; + this.trialLicense = trialLicense; + this.loginStatus = loginStatus; + this.isServerHealthy = isServerHealthy; + this.registrationRequired = registrationRequired; + } + } + + internal class HotReloadSettingsTab : HotReloadTabBase { + private readonly HotReloadOptionsSection optionsSection; + + // cached because changing built target triggers C# domain reload + // Also I suspect selectedBuildTargetGroup has chance to freeze Unity for several seconds (unconfirmed). + private readonly Lazy currentBuildTarget = new Lazy( + () => BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); + + private readonly Lazy isCurrentBuildTargetSupported = new Lazy(() => { + var target = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); + return HotReloadBuildHelper.IsMonoSupported(target); + }); + + // Resources.Load uses cache, so it's safe to call it every frame. + // Retrying Load every time fixes an issue where you import the package and constructor runs, but resources aren't loadable yet. + private Texture iconCheck => Resources.Load("icon_check_circle"); + private Texture iconWarning => Resources.Load("icon_warning_circle"); + + [SuppressMessage("ReSharper", "Unity.UnknownResource")] // Rider doesn't check packages + public HotReloadSettingsTab(HotReloadWindow window) : base(window, + Translations.Settings.SettingsTitle, + "_Popup", + Translations.OnDevice.OnDeviceHeadline) { + optionsSection = new HotReloadOptionsSection(); + } + + private GUIStyle headlineStyle; + private GUIStyle paddedStyle; + + private Vector2 _settingsTabScrollPos; + + HotReloadSettingsTabState currentState; + public override void OnGUI() { + // HotReloadAboutTabState ensures rendering is consistent between Layout and Repaint calls + // Without it errors like this happen: + // ArgumentException: Getting control 2's position in a group with only 2 controls when doing repaint + // See thread for more context: https://answers.unity.com/questions/17718/argumentexception-getting-control-2s-position-in-a.html + if (Event.current.type == EventType.Layout) { + currentState = new HotReloadSettingsTabState( + running: EditorCodePatcher.Running, + trialLicense: EditorCodePatcher.Status != null && (EditorCodePatcher.Status?.isTrial == true), + loginStatus: EditorCodePatcher.Status, + isServerHealthy: ServerHealthCheck.I.IsServerHealthy, + registrationRequired: RedeemLicenseHelper.I.RegistrationRequired + ); + } + using (var scope = new EditorGUILayout.ScrollViewScope(_settingsTabScrollPos, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(Math.Max(HotReloadWindowStyles.windowScreenHeight, 800)), GUILayout.MaxWidth(Math.Max(HotReloadWindowStyles.windowScreenWidth, 800)))) { + _settingsTabScrollPos.x = scope.scrollPosition.x; + _settingsTabScrollPos.y = scope.scrollPosition.y; + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.DynamicSectionHelpTab)) { + GUILayout.Space(10); + if (!EditorCodePatcher.LoginNotRequired + && !currentState.registrationRequired + // Delay showing login in settings to not confuse users that they need to login to use Free trial + && (HotReloadPrefs.RateAppShown + || PackageConst.IsAssetStoreBuild) + ) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionOuterBoxCompact)) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + RenderLicenseInfoSection(); + } + } + } + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionOuterBoxCompact)) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.ShowConfiguration = EditorGUILayout.Foldout(HotReloadPrefs.ShowConfiguration, Translations.Settings.SettingsConfiguration, true, HotReloadWindowStyles.FoldoutStyle); + if (HotReloadPrefs.ShowConfiguration) { + EditorGUILayout.Space(); + + // main section + RenderUnityAutoRefresh(); + using (new EditorGUI.DisabledScope(!EditorCodePatcher.autoRecompileUnsupportedChangesSupported)) { + RenderAutoRecompileUnsupportedChanges(); + if (HotReloadPrefs.AutoRecompileUnsupportedChanges && EditorCodePatcher.autoRecompileUnsupportedChangesSupported) { + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderAutoRecompileUnsupportedChangesImmediately(); + RenderAutoRecompileUnsupportedChangesOnExitPlayMode(); + RenderAutoRecompileUnsupportedChangesInPlayMode(); + RenderAutoRecompileInspectorFieldEdits(); + RenderAutoRecompilePartiallyUnsupportedChanges(); + RenderDisplayNewMonobehaviourMethodsAsPartiallySupported(); + RenderAutoRecompileUnsupportedChangesInEditMode(); + } + } + EditorGUILayout.Space(); + } + RenderAssetRefresh(); + if (HotReloadPrefs.AllAssetChanges) { + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderIncludeShaderChanges(); + } + + EditorGUILayout.Space(); + } + RenderDebuggerCompatibility(); + + // // fields + // RenderShowFeatures(); + // using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + // RenderShowApplyfieldInitializerEditsToExistingClassInstances(); + // + // EditorGUILayout.Space(); + // } + + // visual feedback + if (EditorWindowHelper.supportsNotifications) { + RenderShowNotifications(); + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderShowPatchingNotifications(); + RenderShowCompilingUnsupportedNotifications(); + } + + EditorGUILayout.Space(); + } + + // misc + RenderMiscHeader(); + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderAutoClearTimeline(); + RenderAutostart(); + RenderConsoleWindow(); + + EditorGUILayout.Space(); + } + + EditorGUILayout.Space(); + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.FlexibleSpace(); + HotReloadWindow.RenderShowOnStartup(); + } + } + } + } + } + + if (!EditorCodePatcher.LoginNotRequired && currentState.trialLicense && currentState.running) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionOuterBoxCompact)) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + RenderPromoCodeSection(); + } + } + } + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionOuterBoxCompact)) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + RenderOnDevice(); + } + } + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionOuterBoxCompact)) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBoxWide)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.ShowAdvanced = EditorGUILayout.Foldout(HotReloadPrefs.ShowAdvanced, Translations.Settings.SettingsAdvanced, true, HotReloadWindowStyles.FoldoutStyle); + if (HotReloadPrefs.ShowAdvanced) { + EditorGUILayout.Space(); + + DeactivateHotReload(); + DisableDetailedErrorReporting(); + PauseHotReloadInEditMode(); +#if UNITY_EDITOR_WIN + if (PackageConst.DefaultLocale == RuntimeLocalization.Locale.English) { + UseWatchman(); + } +#endif + } + } + } + } + } + } + } + + void RenderUnityAutoRefresh() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleManageAutoRefresh), HotReloadPrefs.AllowDisableUnityAutoRefresh); + if (newSettings != HotReloadPrefs.AllowDisableUnityAutoRefresh) { + HotReloadPrefs.AllowDisableUnityAutoRefresh = newSettings; + } + string toggleDescription; + if (HotReloadPrefs.AllowDisableUnityAutoRefresh) { + toggleDescription = Translations.Settings.SettingsManageAutoRefreshOn; + } else { + toggleDescription = Translations.Settings.SettingsManageAutoRefreshOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void RenderAssetRefresh() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAssetRefresh), HotReloadPrefs.AllAssetChanges); + if (newSettings != HotReloadPrefs.AllAssetChanges) { + HotReloadPrefs.AllAssetChanges = newSettings; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRestartServer, + Translations.Dialogs.DialogMessageRestartAssetRefresh, + Translations.Dialogs.DialogButtonRestartHotReload, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + string toggleDescription; + if (HotReloadPrefs.AllAssetChanges) { + toggleDescription = Translations.Settings.SettingsAssetRefreshOn; + } else { + toggleDescription = Translations.Settings.SettingsAssetRefreshOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void RenderDebuggerCompatibility() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleDebuggerCompatibility), HotReloadPrefs.AutoDisableHotReloadWithDebugger); + if (newSettings != HotReloadPrefs.AutoDisableHotReloadWithDebugger) { + HotReloadPrefs.AutoDisableHotReloadWithDebugger = newSettings; + CodePatcher.I.debuggerCompatibilityEnabled = !HotReloadPrefs.AutoDisableHotReloadWithDebugger; + } + string toggleDescription; + if (HotReloadPrefs.AutoDisableHotReloadWithDebugger) { + toggleDescription = Translations.Settings.SettingsDebuggerCompatibilityOn; + } else { + toggleDescription = Translations.Settings.SettingsDebuggerCompatibilityOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void RenderIncludeShaderChanges() { + HotReloadPrefs.IncludeShaderChanges = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleRefreshShaders), HotReloadPrefs.IncludeShaderChanges); + string toggleDescription; + if (HotReloadPrefs.IncludeShaderChanges) { + toggleDescription = Translations.Settings.SettingsRefreshShadersOn; + } else { + toggleDescription = Translations.Settings.SettingsRefreshShadersOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderConsoleWindow() { + if (!HotReloadCli.CanOpenInBackground) { + return; + } + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleHideConsole), HotReloadPrefs.DisableConsoleWindow); + if (newSettings != HotReloadPrefs.DisableConsoleWindow) { + HotReloadPrefs.DisableConsoleWindow = newSettings; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRestartServer, + Translations.Dialogs.DialogMessageRestartConsoleWindow, + Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + string toggleDescription; + if (HotReloadPrefs.DisableConsoleWindow) { + toggleDescription = Translations.Settings.SettingsHideConsoleOn; + } else { + toggleDescription = Translations.Settings.SettingsHideConsoleOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void DeactivateHotReload() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleDeactivate), HotReloadPrefs.DeactivateHotReload); + if (newSettings != HotReloadPrefs.DeactivateHotReload) { + DeactivateHotReloadInner(newSettings); + } + string toggleDescription; + if (HotReloadPrefs.DeactivateHotReload) { + toggleDescription = Translations.Settings.SettingsDeactivatedOn; + } else { + toggleDescription = Translations.Settings.SettingsDeactivatedOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void DisableDetailedErrorReporting() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleDisableErrorReporting), HotReloadPrefs.DisableDetailedErrorReporting); + DisableDetailedErrorReportingInner(newSettings); + string toggleDescription; + if (HotReloadPrefs.DisableDetailedErrorReporting) { + toggleDescription = Translations.Settings.SettingsDisableErrorReportingOn; + } else { + toggleDescription = Translations.Settings.SettingsDisableErrorReportingOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + + void PauseHotReloadInEditMode() { + HotReloadPrefs.PauseHotReloadInEditMode = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.TogglePauseEditMode), HotReloadPrefs.PauseHotReloadInEditMode); + string toggleDescription; + if (HotReloadPrefs.PauseHotReloadInEditMode) { + toggleDescription = Translations.Settings.SettingsPauseEditModeOn; + } else { + toggleDescription = Translations.Settings.SettingsPauseEditModeOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } + +#if UNITY_EDITOR_WIN + void UseWatchman() { + HotReloadPrefs.UseWatchman = EditorGUILayout.BeginToggleGroup(new GUIContent("Use watchman"), HotReloadPrefs.UseWatchman); + string toggleDescription; + if (HotReloadPrefs.UseWatchman) { + toggleDescription = "Use watchman file watcher"; + } else { + toggleDescription = "Use default file watcher"; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(6f); + } +#endif + + public static void DisableDetailedErrorReportingInner(bool newSetting) { + if (newSetting == HotReloadPrefs.DisableDetailedErrorReporting) { + return; + } + HotReloadPrefs.DisableDetailedErrorReporting = newSetting; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRestartServer, + Translations.Dialogs.DialogMessageRestartErrorReporting, + Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + + static void DeactivateHotReloadInner(bool deactivate) { + var confirmed = !deactivate || EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleDeactivate, + Translations.Dialogs.DialogMessageDeactivate, + Translations.Dialogs.DialogButtonDeactivate, Translations.Common.ButtonCancel); + if (confirmed) { + HotReloadPrefs.DeactivateHotReload = deactivate; + if (deactivate) { + EditorCodePatcher.StopCodePatcher(recompileOnDone: true).Forget(); + } else { + HotReloadRunTab.Recompile(); + } + } + } + + void RenderAutoClearTimeline() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAutoClearTimeline), HotReloadPrefs.AutoClearTimeline); + if (newSettings != HotReloadPrefs.AutoClearTimeline) { + HotReloadPrefs.AutoClearTimeline = newSettings; + } + string toggleDescription; + if (HotReloadPrefs.AutoClearTimeline) { + toggleDescription = Translations.Settings.SettingsAutoClearTimelineOn; + } else { + toggleDescription = Translations.Settings.SettingsAutoClearTimelineOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(); + } + + void RenderAutostart() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAutostart), HotReloadPrefs.LaunchOnEditorStart); + if (newSettings != HotReloadPrefs.LaunchOnEditorStart) { + HotReloadPrefs.LaunchOnEditorStart = newSettings; + } + string toggleDescription; + if (HotReloadPrefs.LaunchOnEditorStart) { + toggleDescription = Translations.Settings.SettingsAutostartOn; + } else { + toggleDescription = Translations.Settings.SettingsAutostartOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(); + } + + void RenderShowNotifications() { + EditorGUILayout.Space(10f); + GUILayout.Label(Translations.Settings.SettingsVisualFeedback, HotReloadWindowStyles.NotificationsTitleStyle); + EditorGUILayout.Space(10f); + + if (!EditorWindowHelper.supportsNotifications && !UnitySettingsHelper.I.playmodeTintSupported) { + var toggleDescription = Translations.Settings.SettingsIndicationsUnsupported; + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + } + } + + // void RenderShowFields() { + // EditorGUILayout.Space(14f); + // GUILayout.Label("Fields", HotReloadWindowStyles.NotificationsTitleStyle); + // } + + void RenderMiscHeader() { + EditorGUILayout.Space(10f); + GUILayout.Label(Translations.Settings.SettingsMisc, HotReloadWindowStyles.NotificationsTitleStyle); + EditorGUILayout.Space(10f); + } + + void RenderShowPatchingNotifications() { + HotReloadPrefs.ShowPatchingNotifications = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.TogglePatchingIndication), HotReloadPrefs.ShowPatchingNotifications); + string toggleDescription; + if (!EditorWindowHelper.supportsNotifications) { + toggleDescription = Translations.Settings.SettingsPatchingIndicationUnsupported; + } else if (!HotReloadPrefs.ShowPatchingNotifications) { + toggleDescription = Translations.Settings.SettingsPatchingIndicationOff; + } else { + toggleDescription = Translations.Settings.SettingsPatchingIndicationOn; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + // void RenderShowApplyfieldInitializerEditsToExistingClassInstances() { + // var newSetting = EditorGUILayout.BeginToggleGroup(new GUIContent("Apply field initializer edits to existing class instances"), HotReloadPrefs.ApplyFieldInitiailzerEditsToExistingClassInstances); + // ApplyApplyFieldInitializerEditsToExistingClassInstances(newSetting); + // string toggleDescription; + // if (HotReloadPrefs.ApplyFieldInitiailzerEditsToExistingClassInstances) { + // toggleDescription = "New field initializers with constant value will update field value of existing objects."; + // } else { + // toggleDescription = "New field initializers will not modify existing objects."; + // } + // EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + // EditorGUILayout.EndToggleGroup(); + // } + + [Obsolete(Translations.MenuItems.NotImplementedObsolete)] + public static void ApplyApplyFieldInitializerEditsToExistingClassInstances(bool newSetting) { + if (newSetting != HotReloadPrefs.ApplyFieldInitiailzerEditsToExistingClassInstances) { + HotReloadPrefs.ApplyFieldInitiailzerEditsToExistingClassInstances = newSetting; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRestartServer, + Translations.Dialogs.DialogMessageRestartFieldInitializer, + Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + } + + void RenderShowCompilingUnsupportedNotifications() { + HotReloadPrefs.ShowCompilingUnsupportedNotifications = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleCompilingIndication), HotReloadPrefs.ShowCompilingUnsupportedNotifications); + string toggleDescription; + if (!EditorWindowHelper.supportsNotifications) { + toggleDescription = Translations.Settings.SettingsCompilingIndicationUnsupported; + } else if (!HotReloadPrefs.ShowCompilingUnsupportedNotifications) { + toggleDescription = Translations.Settings.SettingsCompilingIndicationOff; + } else { + toggleDescription = Translations.Settings.SettingsCompilingIndicationOn; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChanges() { + HotReloadPrefs.AutoRecompileUnsupportedChanges = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAutoRecompile), HotReloadPrefs.AutoRecompileUnsupportedChanges && EditorCodePatcher.autoRecompileUnsupportedChangesSupported); + string toggleDescription; + if (!EditorCodePatcher.autoRecompileUnsupportedChangesSupported) { + toggleDescription = Translations.Settings.SettingsAutoRecompileUnsupported; + } else if (HotReloadPrefs.AutoRecompileUnsupportedChanges) { + toggleDescription = Translations.Settings.SettingsAutoRecompileOn; + } else { + toggleDescription = Translations.Settings.SettingsAutoRecompileOff; + } + if (!HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode && !HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode) { + HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode = true; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileInspectorFieldEdits() { + HotReloadPrefs.AutoRecompileInspectorFieldsEdit = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAutoRecompileInspector), HotReloadPrefs.AutoRecompileInspectorFieldsEdit); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileInspectorFieldsEdit) { + toggleDescription = Translations.Settings.SettingsAutoRecompileInspectorOn; + } else { + toggleDescription = Translations.Settings.SettingsAutoRecompileInspectorOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompilePartiallyUnsupportedChanges() { + HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleAutoRecompilePartial), HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges); + string toggleDescription; + if (HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges) { + toggleDescription = Translations.Settings.SettingsAutoRecompilePartialOn; + } else { + toggleDescription = Translations.Settings.SettingsAutoRecompilePartialOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderDisplayNewMonobehaviourMethodsAsPartiallySupported() { + HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleDisplayMonobehaviour), HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported); + string toggleDescription; + if (HotReloadPrefs.DisplayNewMonobehaviourMethodsAsPartiallySupported) { + toggleDescription = Translations.Settings.SettingsDisplayMonobehaviourOn; + } else { + toggleDescription = Translations.Settings.SettingsDisplayMonobehaviourOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesImmediately() { + HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleRecompileImmediately), HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately) { + toggleDescription = Translations.Settings.SettingsRecompileImmediatelyOn; + } else { + toggleDescription = Translations.Settings.SettingsRecompileImmediatelyOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesInPlayMode() { + HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleRecompilePlayMode), HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode) { + toggleDescription = Translations.Settings.SettingsRecompilePlayModeOn; + } else { + toggleDescription = Translations.Settings.SettingsRecompilePlayModeOff; + } + if (!HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode && !HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode) { + HotReloadPrefs.AutoRecompileUnsupportedChanges = false; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesInEditMode() { + HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleRecompileEditMode), HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode) { + toggleDescription = Translations.Settings.SettingsRecompileEditModeOn; + } else { + toggleDescription = Translations.Settings.SettingsRecompileEditModeOff; + } + if (!HotReloadPrefs.AutoRecompileUnsupportedChangesInEditMode && !HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode) { + HotReloadPrefs.AutoRecompileUnsupportedChanges = false; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesOnExitPlayMode() { + HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode = EditorGUILayout.BeginToggleGroup(new GUIContent(Translations.Settings.ToggleRecompileExitPlayMode), HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode) { + toggleDescription = Translations.Settings.SettingsRecompileExitPlayModeOn; + } else { + toggleDescription = Translations.Settings.SettingsRecompileExitPlayModeOff; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderOnDevice() { + HotReloadPrefs.ShowOnDevice = EditorGUILayout.Foldout(HotReloadPrefs.ShowOnDevice, Translations.Settings.SettingsOnDevice, true, HotReloadWindowStyles.FoldoutStyle); + if (!HotReloadPrefs.ShowOnDevice) { + return; + } + // header with explainer image + { + if (headlineStyle == null) { + // start with textArea for the background and border colors + headlineStyle = new GUIStyle(GUI.skin.label) { + fontStyle = FontStyle.Bold, + alignment = TextAnchor.MiddleLeft + }; + headlineStyle.normal.textColor = HotReloadWindowStyles.H2TitleStyle.normal.textColor; + + // bg color + if (HotReloadWindowStyles.IsDarkMode) { + headlineStyle.normal.background = EditorTextures.DarkGray40; + } else { + headlineStyle.normal.background = EditorTextures.LightGray225; + } + // layout + headlineStyle.padding = new RectOffset(8, 8, 0, 0); + headlineStyle.margin = new RectOffset(6, 6, 6, 6); + } + GUILayout.Space(9f); // space between logo and headline + + GUILayout.Label(Translations.OnDevice.OnDeviceHeadline, + headlineStyle, GUILayout.MinHeight(EditorGUIUtility.singleLineHeight * 1.4f)); + // image showing how Hot Reload works with a phone + // var bannerBox = GUILayoutUtility.GetRect(flowchart.width * 0.6f, flowchart.height * 0.6f); + // GUI.DrawTexture(bannerBox, flowchart, ScaleMode.ScaleToFit); + } + + GUILayout.Space(16f); + + //ButtonToOpenBuildSettings(); + + { + GUILayout.Label(Translations.Settings.SettingsManualConnect, HotReloadWindowStyles.H3TitleStyle); + EditorGUILayout.Space(); + + GUILayout.BeginHorizontal(); + + // indent all controls (this works with non-labels) + GUILayout.Space(16f); + GUILayout.BeginVertical(); + + string text; + var ip = IpHelper.GetIpAddressCached(); + if (string.IsNullOrEmpty(ip)) { + text = string.Format(Translations.OnDevice.OnDeviceManualConnectFormat, RequestHelper.port); + } else { + text = string.Format(Translations.OnDevice.OnDeviceManualConnectWithIP, ip, RequestHelper.port); + } + GUILayout.Label(text, HotReloadWindowStyles.H3TitleWrapStyle); + + if (!currentState.isServerHealthy) { + DrawHorizontalCheck(ServerHealthCheck.I.IsServerHealthy, + Translations.OnDevice.OnDeviceCheckHotReloadRunning, + Translations.OnDevice.OnDeviceCheckHotReloadNotRunning, + hasFix: false); + } + + if (!HotReloadPrefs.ExposeServerToLocalNetwork) { + var summary = string.Format(Translations.OnDevice.OnDeviceCheckEnableExposeServer, new ExposeServerOption().ShortSummary); + DrawHorizontalCheck(HotReloadPrefs.ExposeServerToLocalNetwork, + summary, + summary); + } + + // explainer image that shows phone needs same wifi to auto connect ? + + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + } + + GUILayout.Space(16f); + + // loading again is smooth, pretty sure AssetDatabase.LoadAssetAtPath is caching -Troy + var settingsObject = HotReloadSettingsEditor.LoadSettingsOrDefault(); + var so = new SerializedObject(settingsObject); + + // if you build for Android now, will Hot Reload work? + { + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label(Translations.Settings.SettingsBuildSettingsChecklist, HotReloadWindowStyles.H3TitleStyle); + EditorGUI.BeginDisabledGroup(isSupported); + // One-click to change each setting to the supported value + if (GUILayout.Button(Translations.Common.ButtonFixAll, GUILayout.MaxWidth(90f))) { + FixAllUnsupportedSettings(so); + } + EditorGUI.EndDisabledGroup(); + EditorGUILayout.EndHorizontal(); + + + // NOTE: After user changed some build settings, window may not immediately repaint + // (e.g. toggle Development Build in Build Settings window) + // We could show a refresh button (to encourage the user to click the window which makes it repaint). + DrawSectionCheckBuildSupport(so); + } + + + GUILayout.Space(16f); + + // Settings checkboxes (Hot Reload options) + { + GUILayout.Label(Translations.Settings.SettingsOptions, HotReloadWindowStyles.H3TitleStyle); + if (settingsObject) { + optionsSection.DrawGUI(so); + } + } + GUILayout.FlexibleSpace(); // needed otherwise vertical scrollbar is appearing for no reason (Unity 2021 glitch perhaps) + } + + private void RenderLicenseInfoSection() { + HotReloadRunTab.RenderLicenseInfo( + _window.RunTabState, + currentState.loginStatus, + verbose: true, + allowHide: false, + overrideActionButton:Translations.Common.ButtonActivateLicense, + showConsumptions: true + ); + } + + private void RenderPromoCodeSection() { + _window.RunTab.RenderPromoCodes(); + } + + public void FocusLicenseFoldout() { + HotReloadPrefs.ShowLogin = true; + } + + // note: changing scripting backend does not force Unity to recreate the GUI, so need to check it when drawing. + private ScriptingImplementation ScriptingBackend => HotReloadBuildHelper.GetCurrentScriptingBackend(); + private ManagedStrippingLevel StrippingLevel => HotReloadBuildHelper.GetCurrentStrippingLevel(); + public bool isSupported = true; + + /// + /// These options are drawn in the On-device tab + /// + // new on-device options should be added here + public static readonly IOption[] allOptions = new IOption[] { + new ExposeServerOption(), + IncludeInBuildOption.I, + new AllowAndroidAppToMakeHttpRequestsOption(), + }; + + /// + /// Change each setting to the value supported by Hot Reload + /// + private void FixAllUnsupportedSettings(SerializedObject so) { + if (!isCurrentBuildTargetSupported.Value) { + // try switch to Android platform + // (we also support Standalone but HotReload on mobile is a better selling point) + if (!TrySwitchToStandalone()) { + // skip changing other options (user won't readthe gray text) - user has to click Fix All again + return; + } + } + + foreach (var buildOption in allOptions) { + if (!buildOption.GetValue(so)) { + buildOption.SetValue(so, true); + } + } + so.ApplyModifiedProperties(); + var settingsObject = so.targetObject as HotReloadSettingsObject; + if (settingsObject) { + // when you click fix all, make sure to save the settings, otherwise ui does not update + HotReloadSettingsEditor.EnsureSettingsCreated(settingsObject); + } + + if (!EditorUserBuildSettings.development) { + EditorUserBuildSettings.development = true; + } + + HotReloadBuildHelper.SetCurrentScriptingBackend(ScriptingImplementation.Mono2x); + HotReloadBuildHelper.SetCurrentStrippingLevel(ManagedStrippingLevel.Disabled); + } + + public static bool TrySwitchToStandalone() { + BuildTarget buildTarget; + if (Application.platform == RuntimePlatform.LinuxEditor) { + buildTarget = BuildTarget.StandaloneLinux64; + } else if (Application.platform == RuntimePlatform.WindowsEditor) { + buildTarget = BuildTarget.StandaloneWindows64; + } else if (Application.platform == RuntimePlatform.OSXEditor) { + buildTarget = BuildTarget.StandaloneOSX; + } else { + return false; + } + var current = EditorUserBuildSettings.activeBuildTarget; + if (current == buildTarget) { + return true; + } + var confirmed = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleSwitchBuildTarget, + Translations.Dialogs.DialogMessageSwitchBuildTarget, + Translations.Dialogs.DialogButtonSwitchToStandalone, Translations.Common.ButtonCancel); + if (confirmed) { + EditorUserBuildSettings.SwitchActiveBuildTargetAsync(BuildTargetGroup.Standalone, buildTarget); + Log.Info(Translations.About.LogBuildTargetSwitching, buildTarget); + return true; + } else { + return false; + } + } + + /// + /// Section that user can check before making a Unity Player build. + /// + /// + /// + /// This section is for confirming your build will work with Hot Reload.
+ /// Options that can be changed after the build is made should be drawn elsewhere. + ///
+ public void DrawSectionCheckBuildSupport(SerializedObject so) { + isSupported = true; + var selectedPlatform = currentBuildTarget.Value; + DrawHorizontalCheck(isCurrentBuildTargetSupported.Value, + string.Format(Translations.OnDevice.OnDeviceCheckPlatformSelected, selectedPlatform.ToString()), + string.Format(Translations.OnDevice.OnDeviceCheckPlatformNotSupported, selectedPlatform.ToString())); + + using (new EditorGUI.DisabledScope(!isCurrentBuildTargetSupported.Value)) { + foreach (var option in allOptions) { + DrawHorizontalCheck(option.GetValue(so), + string.Format(Translations.OnDevice.OnDeviceCheckEnableExposeServer, option.ShortSummary), + string.Format(Translations.OnDevice.OnDeviceCheckEnableExposeServer, option.ShortSummary)); + } + + DrawHorizontalCheck(EditorUserBuildSettings.development, + Translations.OnDevice.OnDeviceCheckDevelopmentEnabled, + Translations.OnDevice.OnDeviceCheckEnableDevelopment); + + DrawHorizontalCheck(ScriptingBackend == ScriptingImplementation.Mono2x, + Translations.OnDevice.OnDeviceCheckMonoBackend, + Translations.OnDevice.OnDeviceCheckSetMonoBackend); + + DrawHorizontalCheck(StrippingLevel == ManagedStrippingLevel.Disabled, + string.Format(Translations.OnDevice.OnDeviceCheckStrippingLevel, StrippingLevel), + string.Format(Translations.OnDevice.OnDeviceCheckStrippingLevel, StrippingLevel), + suggestedSolutionText: Translations.OnDevice.OnDeviceCheckStrippingSolution + ); + } + } + + /// + /// Draw a box with a tick or warning icon on the left, with text describing the tick or warning + /// + /// The condition to check. True to show a tick icon, False to show a warning. + /// Shown when condition is true + /// Shown when condition is false + /// Shown when is false + void DrawHorizontalCheck(bool condition, string okText, string notOkText = null, string suggestedSolutionText = null, bool hasFix = true) { + if (okText == null) { + throw new ArgumentNullException(nameof(okText)); + } + if (notOkText == null) { + notOkText = okText; + } + + // include some horizontal space around the icon + var boxWidth = GUILayout.Width(EditorGUIUtility.singleLineHeight * 1.31f); + var height = GUILayout.Height(EditorGUIUtility.singleLineHeight * 1.01f); + GUILayout.BeginHorizontal(HotReloadWindowStyles.BoxStyle, height, GUILayout.ExpandWidth(true)); + var style = HotReloadWindowStyles.NoPaddingMiddleLeftStyle; + var iconRect = GUILayoutUtility.GetRect( + Mathf.Round(EditorGUIUtility.singleLineHeight * 1.31f), + Mathf.Round(EditorGUIUtility.singleLineHeight * 1.01f), + style, boxWidth, height, GUILayout.ExpandWidth(false)); + // rounded so we can have pixel perfect black circle bg + iconRect.Set(Mathf.Round(iconRect.x), Mathf.Round(iconRect.y), Mathf.CeilToInt(iconRect.width), + Mathf.CeilToInt(iconRect.height)); + var text = condition ? okText : notOkText; + var icon = condition ? iconCheck : iconWarning; + if (GUI.enabled) { + DrawBlackCircle(iconRect); + // resource can be null when building player (Editor Resources not available) + if (icon) { + GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit); + } + } else { + // show something (instead of hiding) so that layout stays same size + DrawDisabledCircle(iconRect); + } + GUILayout.Space(4f); + GUILayout.Label(text, style, height); + + if (!condition && hasFix) { + isSupported = false; + } + + GUILayout.EndHorizontal(); + if (!condition && !String.IsNullOrEmpty(suggestedSolutionText)) { + // suggest to the user how they can resolve the issue + EditorGUI.indentLevel++; + GUILayout.Label(suggestedSolutionText, HotReloadWindowStyles.WrapStyle); + EditorGUI.indentLevel--; + } + } + + void DrawDisabledCircle(Rect rect) => DrawCircleIcon(rect, + Resources.Load("icon_circle_gray"), + Color.clear); // smaller circle draws less attention + + void DrawBlackCircle(Rect rect) => DrawCircleIcon(rect, + Resources.Load("icon_circle_black"), + new Color(0.14f, 0.14f, 0.14f)); // black is too dark in unity light theme + + void DrawCircleIcon(Rect rect, Texture circleIcon, Color borderColor) { + // Note: drawing texture from resources is pixelated on the edges, so it has some transperancy around the edges. + // While building for Android, Resources.Load returns null for our editor Resources. + if (circleIcon != null) { + GUI.DrawTexture(rect, circleIcon, ScaleMode.ScaleToFit); + } + + // Draw smooth circle border + const float borderWidth = 2f; + GUI.DrawTexture(rect, EditorTextures.White, ScaleMode.ScaleToFit, true, + 0f, + borderColor, + new Vector4(borderWidth, borderWidth, borderWidth, borderWidth), + Mathf.Min(rect.height, rect.width) / 2f); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs.meta new file mode 100644 index 0000000..00d31cd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fff71bd159424bf2978e2e99eacba9b4 +timeCreated: 1674057842 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs new file mode 100644 index 0000000..8e782bb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs @@ -0,0 +1,393 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using System.Threading; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Cli; +using SingularityGroup.HotReload.Editor.Semver; +using UnityEditor; +using UnityEditor.Compilation; +using SingularityGroup.HotReload.Editor.Localization; +using UnityEngine; + +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.EditorSamples")] + +namespace SingularityGroup.HotReload.Editor { + class HotReloadWindow : EditorWindow { + public static HotReloadWindow Current { get; private set; } + + List tabs; + List Tabs => tabs ?? (tabs = new List { + RunTab, + SettingsTab, + AboutTab, + }); + int selectedTab; + + internal static Vector2 scrollPos; + + static Timer timer; + + + HotReloadRunTab runTab; + internal HotReloadRunTab RunTab => runTab ?? (runTab = new HotReloadRunTab(this)); + HotReloadSettingsTab settingsTab; + internal HotReloadSettingsTab SettingsTab => settingsTab ?? (settingsTab = new HotReloadSettingsTab(this)); + HotReloadAboutTab aboutTab; + internal HotReloadAboutTab AboutTab => aboutTab ?? (aboutTab = new HotReloadAboutTab(this)); + + static ShowOnStartupEnum _showOnStartupOption; + + /// + /// This token is cancelled when the EditorWindow is disabled. + /// + /// + /// Use it for all tasks. + /// When token is cancelled, scripts are about to be recompiled and this will cause tasks to fail for weird reasons. + /// + public CancellationToken cancelToken; + CancellationTokenSource cancelTokenSource; + + static readonly PackageUpdateChecker packageUpdateChecker = new PackageUpdateChecker(); + + [MenuItem(Translations.MenuItems.OpenHotReload)] + internal static void Open() { + // Don't open Hot Reload window inside Virtual Player folder + if (MultiplayerPlaymodeHelper.IsClone) { + Log.Info("Virtual Player instances use the same Hot Reload server instance as the Main Editor. Use Hot Reload window in the Main Editor."); + return; + } + // opening the window on CI systems was keeping Unity open indefinitely + if (EditorWindowHelper.IsHumanControllingUs()) { + if (Current) { + Current.Show(); + Current.Focus(); + } else { + Current = GetWindow(); + } + } + } + + [MenuItem(Translations.MenuItems.RecompileHotReload)] + internal static void Recompile() { + HotReloadRunTab.Recompile(); + } + + void OnInterval(object o) { + HotReloadRunTab.RepaintInstant(); + } + + void OnEnable() { + if (timer == null) { + timer = new Timer(OnInterval, null, 20 * 1000, 20 * 1000); + } + Current = this; + if (cancelTokenSource != null) { + cancelTokenSource.Cancel(); + } + // Set min size initially so that full UI is visible + if (!HotReloadPrefs.OpenedWindowAtLeastOnce) { + this.minSize = new Vector2(Constants.RecompileButtonTextHideWidth + 1, Constants.EventsListHideHeight + 70); + HotReloadPrefs.OpenedWindowAtLeastOnce = true; + } + cancelTokenSource = new CancellationTokenSource(); + cancelToken = cancelTokenSource.Token; + + this.titleContent = new GUIContent(" Hot Reload", GUIHelper.GetInvertibleIcon(InvertibleIcon.Logo)); + _showOnStartupOption = HotReloadPrefs.ShowOnStartup; + + packageUpdateChecker.StartCheckingForNewVersion(); + } + + void Update() { + foreach (var tab in Tabs) { + tab.Update(); + } + } + + void OnDisable() { + if (cancelTokenSource != null) { + cancelTokenSource.Cancel(); + cancelTokenSource = null; + } + + if (Current == this) { + Current = null; + } + timer.Dispose(); + timer = null; + } + + internal void SelectTab(Type tabType) { + selectedTab = Tabs.FindIndex(x => x.GetType() == tabType); + } + + public HotReloadRunTabState RunTabState { get; private set; } + void OnGUI() { + // TabState ensures rendering is consistent between Layout and Repaint calls + // Without it errors like this happen: + // ArgumentException: Getting control 2's position in a group with only 2 controls when doing repaint + // See thread for more context: https://answers.unity.com/questions/17718/argumentexception-getting-control-2s-position-in-a.html + if (Event.current.type == EventType.Layout) { + RunTabState = HotReloadRunTabState.Current; + } + using(var scope = new EditorGUILayout.ScrollViewScope(scrollPos, false, false)) { + scrollPos = scope.scrollPosition; + // RenderDebug(); + RenderTabs(); + } + GUILayout.FlexibleSpace(); // GUI below will be rendered on the bottom + if (HotReloadWindowStyles.windowScreenHeight > 90) + RenderBottomBar(); + } + + void RenderDebug() { + if (GUILayout.Button("RESET WINDOW")) { + OnDisable(); + + RequestHelper.RequestLogin("test", "test", 1).Forget(); + + HotReloadPrefs.LicenseEmail = null; + HotReloadPrefs.ExposeServerToLocalNetwork = true; + HotReloadPrefs.LicensePassword = null; + HotReloadPrefs.LoggedBurstHint = false; + HotReloadPrefs.DontShowPromptForDownload = false; + HotReloadPrefs.RateAppShown = false; + HotReloadPrefs.ActiveDays = string.Empty; + HotReloadPrefs.LaunchOnEditorStart = false; + HotReloadPrefs.ShowUnsupportedChanges = true; + HotReloadPrefs.RedeemLicenseEmail = null; + HotReloadPrefs.RedeemLicenseInvoice = null; + OnEnable(); + File.Delete(EditorCodePatcher.serverDownloader.GetExecutablePath(HotReloadCli.controller)); + InstallUtility.DebugClearInstallState(); + InstallUtility.CheckForNewInstall(); + EditorPrefs.DeleteKey(Attribution.LastLoginKey); + File.Delete(RedeemLicenseHelper.registerOutcomePath); + + CompileMethodDetourer.Reset(); + AssetDatabase.Refresh(); + } + } + + internal static void RenderLogo(int width = 243) { + var isDarkMode = HotReloadWindowStyles.IsDarkMode; + var tex = Resources.Load(isDarkMode ? "Logo_HotReload_DarkMode" : "Logo_HotReload_LightMode"); + //Can happen during player builds where Editor Resources are unavailable + if(tex == null) { + return; + } + var targetWidth = width; + var targetHeight = 44; + GUILayout.Space(4f); + // background padding top and bottom + float padding = 5f; + // reserve layout space for the texture + var backgroundRect = GUILayoutUtility.GetRect(targetWidth + padding, targetHeight + padding, HotReloadWindowStyles.LogoStyle); + // draw the texture into that reserved space. First the bg then the logo. + if (isDarkMode) { + GUI.DrawTexture(backgroundRect, EditorTextures.DarkGray17, ScaleMode.StretchToFill); + } else { + GUI.DrawTexture(backgroundRect, EditorTextures.LightGray238, ScaleMode.StretchToFill); + } + + var foregroundRect = backgroundRect; + foregroundRect.yMin += padding; + foregroundRect.yMax -= padding; + // during player build (EditorWindow still visible), Resources.Load returns null + if (tex) { + GUI.DrawTexture(foregroundRect, tex, ScaleMode.ScaleToFit); + } + } + + int? collapsedTab; + void RenderTabs() { + using(new EditorGUILayout.VerticalScope(HotReloadWindowStyles.BoxStyle)) { + if (HotReloadWindowStyles.windowScreenHeight > 210 && HotReloadWindowStyles.windowScreenWidth > 375) { + selectedTab = GUILayout.Toolbar( + selectedTab, + Tabs.Select(t => + new GUIContent(t.Title.StartsWith(" ", StringComparison.Ordinal) ? t.Title : " " + t.Title, + t.Icon, t.Tooltip)).ToArray(), + GUILayout.Height(22f) // required, otherwise largest icon height determines toolbar height + ); + if (collapsedTab != null) { + selectedTab = collapsedTab.Value; + collapsedTab = null; + } + } else { + if (collapsedTab == null) { + collapsedTab = selectedTab; + } + // When window is super small, we pretty much can only show run tab + SelectTab(typeof(HotReloadRunTab)); + } + + if (HotReloadWindowStyles.windowScreenHeight > 250 && HotReloadWindowStyles.windowScreenWidth > 275) { + RenderLogo(); + } + + Tabs[selectedTab].OnGUI(); + } + } + + void RenderBottomBar() { + SemVersion newVersion; + var updateAvailable = packageUpdateChecker.TryGetNewVersion(out newVersion); + + if (HotReloadWindowStyles.windowScreenWidth > Constants.RateAppHideWidth + && HotReloadWindowStyles.windowScreenHeight > Constants.RateAppHideHeight + ) { + RenderRateApp(); + } + + if (updateAvailable) { + RenderUpdateButton(newVersion); + } + + using(new EditorGUILayout.HorizontalScope("ProjectBrowserBottomBarBg", GUILayout.ExpandWidth(true), GUILayout.Height(25f))) { + RenderBottomBarCore(); + } + } + + static GUIStyle _renderAppBoxStyle; + static GUIStyle renderAppBoxStyle => _renderAppBoxStyle ?? (_renderAppBoxStyle = new GUIStyle(GUI.skin.box) { + padding = new RectOffset(10, 10, 0, 0) + }); + + static GUILayoutOption[] _nonExpandable; + public static GUILayoutOption[] NonExpandableLayout => _nonExpandable ?? (_nonExpandable = new [] {GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true)}); + + internal static void RenderRateApp() { + if (!ShouldShowRateApp()) { + return; + } + using (new EditorGUILayout.VerticalScope(renderAppBoxStyle)) { + using (new EditorGUILayout.HorizontalScope()) { + HotReloadGUIHelper.HelpBox(Translations.Miscellaneous.RateAppQuestion, MessageType.Info, 11); + if (GUILayout.Button(Translations.Common.ButtonHide, NonExpandableLayout)) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), new EditorExtraData { { "dismissed", true } }).Forget(); + HotReloadPrefs.RateAppShown = true; + } + } + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(Translations.Common.ButtonYes)) { + var openedUrl = PackageConst.IsAssetStoreBuild && EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleRateApp, Translations.Dialogs.DialogMessageRateApp, Translations.Common.ButtonOpenInBrowser, Translations.Common.ButtonCancel); + if (openedUrl) { + Application.OpenURL(Constants.UnityStoreRateAppURL); + } + HotReloadPrefs.RateAppShown = true; + var data = new EditorExtraData(); + if (PackageConst.IsAssetStoreBuild) { + data.Add("opened_url", openedUrl); + } + data.Add("enjoy_app", true); + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), data).Forget(); + } + if (GUILayout.Button(Translations.Common.ButtonNo)) { + HotReloadPrefs.RateAppShown = true; + var data = new EditorExtraData(); + data.Add("enjoy_app", false); + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), data).Forget(); + } + } + } + } + + internal static bool ShouldShowRateApp() { + if (HotReloadPrefs.RateAppShown) { + return false; + } + var activeDays = EditorCodePatcher.GetActiveDaysForRateApp(); + if (activeDays.Count < Constants.DaysToRateApp) { + return false; + } + return true; + } + + void RenderUpdateButton(SemVersion newVersion) { + if (GUILayout.Button(string.Format(Translations.Miscellaneous.ButtonUpdateToVersionFormat, newVersion), HotReloadWindowStyles.UpgradeButtonStyle)) { + packageUpdateChecker.UpdatePackageAsync(newVersion).Forget(CancellationToken.None); + } + } + + internal static void RenderShowOnStartup() { + var prevLabelWidth = EditorGUIUtility.labelWidth; + try { + EditorGUIUtility.labelWidth = 105f; + using (new GUILayout.VerticalScope()) { + using (new GUILayout.HorizontalScope()) { + GUILayout.Label(Translations.Common.LabelShowOnStartup); + Rect buttonRect = GUILayoutUtility.GetLastRect(); + if (EditorGUILayout.DropdownButton(new GUIContent(Regex.Replace(_showOnStartupOption.ToString(), "([a-z])([A-Z])", "$1 $2")), FocusType.Passive, GUILayout.Width(110f))) { + GenericMenu menu = new GenericMenu(); + foreach (ShowOnStartupEnum option in Enum.GetValues(typeof(ShowOnStartupEnum))) { + menu.AddItem(new GUIContent(Regex.Replace(option.ToString(), "([a-z])([A-Z])", "$1 $2")), false, () => { + if (_showOnStartupOption != option) { + _showOnStartupOption = option; + HotReloadPrefs.ShowOnStartup = _showOnStartupOption; + } + }); + } + menu.DropDown(new Rect(buttonRect.x, buttonRect.y, 100, 0)); + } + } + } + } finally { + EditorGUIUtility.labelWidth = prevLabelWidth; + } + } + + void RenderBottomBarCore() { + bool troubleshootingShown = EditorCodePatcher.Started && HotReloadWindowStyles.windowScreenWidth >= 400; + bool alertsShown = EditorCodePatcher.Started && HotReloadWindowStyles.windowScreenWidth > Constants.EventFiltersShownHideWidth; + using (new EditorGUILayout.VerticalScope()) { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.FooterStyle)) { + if (!troubleshootingShown) { + GUILayout.FlexibleSpace(); + if (alertsShown) { + GUILayout.Space(-20); + } + } else { + GUILayout.Space(21); + } + GUILayout.Space(0); + var lastRect = GUILayoutUtility.GetLastRect(); + // show events button when scrolls are hidden + if (!HotReloadRunTab.CanRenderBars(RunTabState) && !RunTabState.starting) { + using (new EditorGUILayout.VerticalScope()) { + GUILayout.FlexibleSpace(); + var icon = HotReloadState.ShowingRedDot ? InvertibleIcon.EventsNew : InvertibleIcon.Events; + if (GUILayout.Button(new GUIContent("", GUIHelper.GetInvertibleIcon(icon)))) { + PopupWindow.Show(new Rect(lastRect.x, lastRect.y, 0, 0), HotReloadEventPopup.I); + } + GUILayout.FlexibleSpace(); + } + GUILayout.Space(3f); + } + if (alertsShown) { + using (new EditorGUILayout.VerticalScope()) { + GUILayout.FlexibleSpace(); + HotReloadTimelineHelper.RenderAlertFilters(); + GUILayout.FlexibleSpace(); + } + } + + GUILayout.FlexibleSpace(); + if (troubleshootingShown) { + using (new EditorGUILayout.VerticalScope()) { + GUILayout.FlexibleSpace(); + OpenURLButton.Render(Translations.Miscellaneous.ButtonTroubleshooting, Constants.TroubleshootingURL); + GUILayout.FlexibleSpace(); + } + GUILayout.Space(21); + } + } + } + } + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs.meta new file mode 100644 index 0000000..c9d5bf0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f62a84c0b148b0a4582bdd9f1a69e6d3 +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/Window/HotReloadWindow.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs new file mode 100644 index 0000000..17f2f50 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs @@ -0,0 +1,7 @@ +namespace SingularityGroup.HotReload.Editor { + enum ShowOnStartupEnum { + Always, + OnNewVersion, + Never, + } +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs.meta new file mode 100644 index 0000000..5038590 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 809f47245f717ad41996974be2443feb +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/Window/ShowOnStartupEnum.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/Styles.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles.meta new file mode 100644 index 0000000..48002b6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 83e25ceea0bb7cd4ebf04b724bb0584c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs new file mode 100644 index 0000000..4f4ede9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs @@ -0,0 +1,777 @@ +using UnityEditor; +using UnityEngine; +using System.Reflection; + +namespace SingularityGroup.HotReload.Editor { + internal static class HotReloadWindowStyles { + private static GUIStyle h1TitleStyle; + private static GUIStyle h1TitleCenteredStyle; + private static GUIStyle h2TitleStyle; + private static GUIStyle h3TitleStyle; + private static GUIStyle h3TitleWrapStyle; + private static GUIStyle h4TitleStyle; + private static GUIStyle h5TitleStyle; + private static GUIStyle boxStyle; + private static GUIStyle wrapStyle; + private static GUIStyle noPaddingMiddleLeftStyle; + private static GUIStyle middleLeftStyle; + private static GUIStyle middleCenterStyle; + private static GUIStyle mediumMiddleCenterStyle; + private static GUIStyle textFieldWrapStyle; + private static GUIStyle foldoutStyle; + private static GUIStyle h3CenterTitleStyle; + private static GUIStyle logoStyle; + private static GUIStyle changelogPointersStyle; + private static GUIStyle recompileButtonStyle; + private static GUIStyle indicationIconStyle; + private static GUIStyle indicationAlertIconStyle; + private static GUIStyle startButtonStyle; + private static GUIStyle stopButtonStyle; + private static GUIStyle eventFilters; + private static GUIStyle sectionOuterBoxCompactStyle; + private static GUIStyle sectionInnerBoxStyle; + private static GUIStyle sectionInnerBoxWideStyle; + private static GUIStyle changelogSectionInnerBoxStyle; + private static GUIStyle indicationBoxStyle; + private static GUIStyle linkStyle; + private static GUIStyle labelStyle; + private static GUIStyle progressBarBarStyle; + private static GUIStyle section; + private static GUIStyle scroll; + private static GUIStyle barStyle; + private static GUIStyle barBgStyle; + private static GUIStyle barChildStyle; + private static GUIStyle barFoldoutStyle; + private static GUIStyle timestampStyle; + private static GUIStyle clickableLabelBoldStyle; + private static GUIStyle _footerStyle; + private static GUIStyle _emptyListText; + private static GUIStyle _stacktraceTextAreaStyle; + private static GUIStyle _customFoldoutStyle; + private static GUIStyle _entryBoxStyle; + private static GUIStyle _childEntryBoxStyle; + private static GUIStyle _removeIconStyle; + private static GUIStyle upgradeLicenseButtonStyle; + private static GUIStyle upgradeLicenseButtonOverlayStyle; + private static GUIStyle upgradeButtonStyle; + private static GUIStyle hideButtonStyle; + private static GUIStyle dynamicSection; + private static GUIStyle dynamicSectionHelpTab; + private static GUIStyle helpTabButton; + private static GUIStyle indicationHelpBox; + private static GUIStyle notificationsTitleStyle; + + private static Color32? darkModeLinkColor; + private static Color32? lightModeModeLinkColor; + + public static bool IsDarkMode => EditorGUIUtility.isProSkin; + public static int windowScreenWidth => HotReloadWindow.Current ? (int)HotReloadWindow.Current.position.width : Screen.width; + public static int windowScreenHeight => HotReloadWindow.Current ? (int)HotReloadWindow.Current.position.height : Screen.height; + public static GUIStyle H1TitleStyle { + get { + if (h1TitleStyle == null) { + h1TitleStyle = new GUIStyle(EditorStyles.label); + h1TitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h1TitleStyle.fontStyle = FontStyle.Bold; + h1TitleStyle.fontSize = 16; + h1TitleStyle.padding.top = 5; + h1TitleStyle.padding.bottom = 5; + } + return h1TitleStyle; + } + } + + public static GUIStyle FooterStyle { + get { + if (_footerStyle == null) { + _footerStyle = new GUIStyle(); + _footerStyle.fixedHeight = 28; + } + return _footerStyle; + } + } + + public static GUIStyle H1TitleCenteredStyle { + get { + if (h1TitleCenteredStyle == null) { + h1TitleCenteredStyle = new GUIStyle(H1TitleStyle); + h1TitleCenteredStyle.alignment = TextAnchor.MiddleCenter; + } + return h1TitleCenteredStyle; + } + } + + public static GUIStyle H2TitleStyle { + get { + if (h2TitleStyle == null) { + h2TitleStyle = new GUIStyle(EditorStyles.label); + h2TitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h2TitleStyle.fontStyle = FontStyle.Bold; + h2TitleStyle.fontSize = 14; + h2TitleStyle.padding.top = 5; + h2TitleStyle.padding.bottom = 5; + } + return h2TitleStyle; + } + } + + public static GUIStyle H3TitleStyle { + get { + if (h3TitleStyle == null) { + h3TitleStyle = new GUIStyle(EditorStyles.label); + h3TitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h3TitleStyle.fontStyle = FontStyle.Bold; + h3TitleStyle.fontSize = 12; + h3TitleStyle.padding.top = 5; + h3TitleStyle.padding.bottom = 5; + } + return h3TitleStyle; + } + } + + public static GUIStyle NotificationsTitleStyle { + get { + if (notificationsTitleStyle == null) { + notificationsTitleStyle = new GUIStyle(HotReloadWindowStyles.H3TitleStyle); + notificationsTitleStyle.padding.bottom = 0; + notificationsTitleStyle.padding.top = 0; + } + return notificationsTitleStyle; + } + } + + public static GUIStyle H3TitleWrapStyle { + get { + if (h3TitleWrapStyle == null) { + h3TitleWrapStyle = new GUIStyle(H3TitleStyle); + h3TitleWrapStyle.wordWrap = true; + } + return h3TitleWrapStyle; + } + } + + public static GUIStyle H3CenteredTitleStyle { + get { + if (h3CenterTitleStyle == null) { + h3CenterTitleStyle = new GUIStyle(EditorStyles.label); + h3CenterTitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h3CenterTitleStyle.fontStyle = FontStyle.Bold; + h3CenterTitleStyle.alignment = TextAnchor.MiddleCenter; + h3CenterTitleStyle.fontSize = 12; + } + return h3CenterTitleStyle; + } + } + + public static GUIStyle H4TitleStyle { + get { + if (h4TitleStyle == null) { + h4TitleStyle = new GUIStyle(EditorStyles.label); + h4TitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h4TitleStyle.fontStyle = FontStyle.Bold; + h4TitleStyle.fontSize = 11; + } + return h4TitleStyle; + } + } + + public static GUIStyle H5TitleStyle { + get { + if (h5TitleStyle == null) { + h5TitleStyle = new GUIStyle(EditorStyles.label); + h5TitleStyle.normal.textColor = EditorStyles.label.normal.textColor; + h5TitleStyle.fontStyle = FontStyle.Bold; + h5TitleStyle.fontSize = 10; + } + return h5TitleStyle; + } + } + + public static GUIStyle LabelStyle { + get { + if (labelStyle == null) { + labelStyle = new GUIStyle(EditorStyles.label); + labelStyle.fontSize = 12; + labelStyle.clipping = TextClipping.Clip; + labelStyle.wordWrap = true; + } + return labelStyle; + } + } + + public static GUIStyle BoxStyle { + get { + if (boxStyle == null) { + boxStyle = new GUIStyle(EditorStyles.helpBox); + boxStyle.normal.textColor = GUI.skin.label.normal.textColor; + boxStyle.fontStyle = FontStyle.Bold; + boxStyle.alignment = TextAnchor.UpperLeft; + } + if (!IsDarkMode) { + boxStyle.normal.background = Texture2D.blackTexture; + } + return boxStyle; + } + } + + public static GUIStyle WrapStyle { + get { + if (wrapStyle == null) { + wrapStyle = new GUIStyle(EditorStyles.label); + wrapStyle.fontStyle = FontStyle.Normal; + wrapStyle.wordWrap = true; + } + return wrapStyle; + } + } + + public static GUIStyle NoPaddingMiddleLeftStyle { + get { + if (noPaddingMiddleLeftStyle == null) { + noPaddingMiddleLeftStyle = new GUIStyle(EditorStyles.label); + noPaddingMiddleLeftStyle.normal.textColor = GUI.skin.label.normal.textColor; + noPaddingMiddleLeftStyle.padding = new RectOffset(); + noPaddingMiddleLeftStyle.margin = new RectOffset(); + noPaddingMiddleLeftStyle.alignment = TextAnchor.MiddleLeft; + } + return noPaddingMiddleLeftStyle; + } + } + + public static GUIStyle MiddleLeftStyle { + get { + if (middleLeftStyle == null) { + middleLeftStyle = new GUIStyle(EditorStyles.label); + middleLeftStyle.fontStyle = FontStyle.Normal; + middleLeftStyle.alignment = TextAnchor.MiddleLeft; + } + + return middleLeftStyle; + } + } + + public static GUIStyle MiddleCenterStyle { + get { + if (middleCenterStyle == null) { + middleCenterStyle = new GUIStyle(EditorStyles.label); + middleCenterStyle.fontStyle = FontStyle.Normal; + middleCenterStyle.alignment = TextAnchor.MiddleCenter; + } + return middleCenterStyle; + } + } + + public static GUIStyle MediumMiddleCenterStyle { + get { + if (mediumMiddleCenterStyle == null) { + mediumMiddleCenterStyle = new GUIStyle(EditorStyles.label); + mediumMiddleCenterStyle.fontStyle = FontStyle.Normal; + mediumMiddleCenterStyle.fontSize = 12; + mediumMiddleCenterStyle.alignment = TextAnchor.MiddleCenter; + } + return mediumMiddleCenterStyle; + } + } + + public static GUIStyle TextFieldWrapStyle { + get { + if (textFieldWrapStyle == null) { + textFieldWrapStyle = new GUIStyle(EditorStyles.textField); + textFieldWrapStyle.wordWrap = true; + } + return textFieldWrapStyle; + } + } + + public static GUIStyle FoldoutStyle { + get { + if (foldoutStyle == null) { + foldoutStyle = new GUIStyle(EditorStyles.foldout); + foldoutStyle.normal.textColor = GUI.skin.label.normal.textColor; + foldoutStyle.alignment = TextAnchor.MiddleLeft; + foldoutStyle.fontStyle = FontStyle.Bold; + foldoutStyle.fontSize = 12; + } + return foldoutStyle; + } + } + + public static GUIStyle LogoStyle { + get { + if (logoStyle == null) { + logoStyle = new GUIStyle(); + logoStyle.margin = new RectOffset(6, 6, 0, 0); + logoStyle.padding = new RectOffset(16, 16, 0, 0); + } + return logoStyle; + } + } + + public static GUIStyle ChangelogPointerStyle { + get { + if (changelogPointersStyle == null) { + changelogPointersStyle = new GUIStyle(EditorStyles.label); + changelogPointersStyle.wordWrap = true; + changelogPointersStyle.fontSize = 12; + changelogPointersStyle.padding.left = 20; + } + return changelogPointersStyle; + } + } + + public static GUIStyle IndicationIcon { + get { + if (indicationIconStyle == null) { + indicationIconStyle = new GUIStyle(H2TitleStyle); + indicationIconStyle.fixedHeight = 20; + } + indicationIconStyle.padding = new RectOffset(left: windowScreenWidth > Constants.IndicationTextHideWidth ? 7 : 5, right: windowScreenWidth > Constants.IndicationTextHideWidth ? 0 : -10, top: 1, bottom: 1); + return indicationIconStyle; + } + } + + public static GUIStyle IndicationAlertIcon { + get { + if (indicationAlertIconStyle == null) { + indicationAlertIconStyle = new GUIStyle(H2TitleStyle); + indicationAlertIconStyle.padding = new RectOffset(left: 5, right: -7, top: 1, bottom: 1); + indicationAlertIconStyle.fixedHeight = 20; + } + return indicationAlertIconStyle; + } + } + + public static GUIStyle RecompileButton { + get { + if (recompileButtonStyle == null) { + recompileButtonStyle = new GUIStyle(EditorStyles.miniButton); + recompileButtonStyle.margin.top = 17; + recompileButtonStyle.fixedHeight = 25; + recompileButtonStyle.margin.right = 5; + } + recompileButtonStyle.fixedWidth = windowScreenWidth > Constants.RecompileButtonTextHideWidth ? 95 : 30; + return recompileButtonStyle; + } + } + + public static GUIStyle StartButton { + get { + if (startButtonStyle == null) { + startButtonStyle = new GUIStyle(EditorStyles.miniButton); + startButtonStyle.fixedHeight = 25; + startButtonStyle.padding.top = 6; + startButtonStyle.padding.bottom = 6; + startButtonStyle.margin.top = 17; + } + startButtonStyle.fixedWidth = windowScreenWidth > Constants.StartButtonTextHideWidth ? 70 : 30; + return startButtonStyle; + } + } + + public static GUIStyle StopButton { + get { + if (stopButtonStyle == null) { + stopButtonStyle = new GUIStyle(EditorStyles.miniButton); + stopButtonStyle.fixedHeight = 25; + stopButtonStyle.margin.top = 17; + } + stopButtonStyle.fixedWidth = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? 70 : 30; + return stopButtonStyle; + } + } + + internal static GUIStyle EventFiltersStyle { + get { + if (eventFilters == null) { + eventFilters = new GUIStyle(EditorStyles.toolbarButton); + eventFilters.fontSize = 13; + // gets overwritten to content size + eventFilters.fixedHeight = 26; + eventFilters.fixedWidth = 50; + eventFilters.margin = new RectOffset(0, 0, 0, 0); + eventFilters.padding = new RectOffset(0, 0, 6, 6); + } + return eventFilters; + } + } + + private static Texture2D _clearBackground; + private static Texture2D clearBackground { + get { + if (_clearBackground == null) { + _clearBackground = new Texture2D(1, 1); + _clearBackground.SetPixel(0, 0, Color.clear); + _clearBackground.Apply(); + } + return _clearBackground; + + } + } + + public static GUIStyle SectionOuterBoxCompact { + get { + if (sectionOuterBoxCompactStyle == null) { + sectionOuterBoxCompactStyle = new GUIStyle(); + sectionOuterBoxCompactStyle.padding.top = 10; + sectionOuterBoxCompactStyle.padding.bottom = 10; + } + // Looks better without a background + sectionOuterBoxCompactStyle.normal.background = clearBackground; + return sectionOuterBoxCompactStyle; + } + } + + public static GUIStyle SectionInnerBox { + get { + if (sectionInnerBoxStyle == null) { + sectionInnerBoxStyle = new GUIStyle(); + } + sectionInnerBoxStyle.padding = new RectOffset(left: 0, right: 0, top: 15, bottom: 0); + return sectionInnerBoxStyle; + } + } + + public static GUIStyle SectionInnerBoxWide { + get { + if (sectionInnerBoxWideStyle == null) { + sectionInnerBoxWideStyle = new GUIStyle(EditorStyles.helpBox); + sectionInnerBoxWideStyle.padding.top = 15; + sectionInnerBoxWideStyle.padding.bottom = 15; + sectionInnerBoxWideStyle.padding.left = 10; + sectionInnerBoxWideStyle.padding.right = 10; + } + return sectionInnerBoxWideStyle; + } + } + + public static GUIStyle DynamiSection { + get { + if (dynamicSection == null) { + dynamicSection = new GUIStyle(); + } + var defaultPadding = 13; + if (windowScreenWidth > 600) { + var dynamicPadding = (windowScreenWidth - 600) / 2; + dynamicSection.padding.left = defaultPadding + dynamicPadding; + dynamicSection.padding.right = defaultPadding + dynamicPadding; + } else if (windowScreenWidth < Constants.IndicationTextHideWidth) { + dynamicSection.padding.left = 0; + dynamicSection.padding.right = 0; + } else { + dynamicSection.padding.left = 13; + dynamicSection.padding.right = 13; + } + return dynamicSection; + } + } + + public static GUIStyle DynamicSectionHelpTab { + get { + if (dynamicSectionHelpTab == null) { + dynamicSectionHelpTab = new GUIStyle(DynamiSection); + } + dynamicSectionHelpTab.padding.left = DynamiSection.padding.left - 3; + dynamicSectionHelpTab.padding.right = DynamiSection.padding.right - 3; + return dynamicSectionHelpTab; + } + } + + public static GUIStyle ChangelogSectionInnerBox { + get { + if (changelogSectionInnerBoxStyle == null) { + changelogSectionInnerBoxStyle = new GUIStyle(EditorStyles.helpBox); + changelogSectionInnerBoxStyle.margin.bottom = 10; + changelogSectionInnerBoxStyle.margin.top = 10; + } + return changelogSectionInnerBoxStyle; + } + } + + public static GUIStyle IndicationBox { + get { + if (indicationBoxStyle == null) { + indicationBoxStyle = new GUIStyle(); + } + indicationBoxStyle.margin.bottom = windowScreenWidth < 141 ? 0 : 10; + return indicationBoxStyle; + } + } + + + public static GUIStyle LinkStyle { + get { + if (linkStyle == null) { + linkStyle = new GUIStyle(EditorStyles.label); + linkStyle.fontStyle = FontStyle.Bold; + } + var color = IsDarkMode ? DarkModeLinkColor : LightModeModeLinkColor; + linkStyle.normal.textColor = color; + return linkStyle; + } + } + + private static Color32 DarkModeLinkColor { + get { + if (darkModeLinkColor == null) { + darkModeLinkColor = new Color32(0x3F, 0x9F, 0xFF, 0xFF); + } + return darkModeLinkColor.Value; + } + } + + + private static Color32 LightModeModeLinkColor { + get { + if (lightModeModeLinkColor == null) { + lightModeModeLinkColor = new Color32(0x0F, 0x52, 0xD7, 0xFF); + } + return lightModeModeLinkColor.Value; + } + } + public static GUIStyle ProgressBarBarStyle { + get { + if (progressBarBarStyle != null) { + return progressBarBarStyle; + } + var styles = (EditorStyles)typeof(EditorStyles) + .GetField("s_Current", BindingFlags.Static | BindingFlags.NonPublic) + ?.GetValue(null); + var style = styles?.GetType() + .GetField("m_ProgressBarBar", BindingFlags.NonPublic | BindingFlags.Instance) + ?.GetValue(styles); + progressBarBarStyle = style != null ? (GUIStyle)style : GUIStyle.none; + return progressBarBarStyle; + } + } + + internal static GUIStyle Section { + get { + if (section == null) { + section = new GUIStyle(EditorStyles.helpBox); + section.padding = new RectOffset(left: 10, right: 10, top: 10, bottom: 10); + section.margin = new RectOffset(left: 0, right: 0, top: 0, bottom: 0); + } + return section; + } + } + internal static GUIStyle Scroll { + get { + if (scroll == null) { + scroll = new GUIStyle(EditorStyles.helpBox); + } + if (IsDarkMode) { + scroll.normal.background = GUIHelper.ConvertTextureToColor(new Color(0,0,0,0.05f)); + } else { + scroll.normal.background = GUIHelper.ConvertTextureToColor(new Color(0,0,0,0.03f)); + } + return scroll; + } + } + + internal static GUIStyle BarStyle { + get { + if (barStyle == null) { + barStyle = new GUIStyle(GUI.skin.label); + barStyle.fontSize = 12; + barStyle.alignment = TextAnchor.MiddleLeft; + barStyle.fixedHeight = 20; + barStyle.padding = new RectOffset(10, 5, 2, 2); + } + return barStyle; + } + } + + internal static GUIStyle BarBackgroundStyle { + get { + if (barBgStyle == null) { + barBgStyle = new GUIStyle(); + } + barBgStyle.normal.background = GUIHelper.ConvertTextureToColor(Color.clear); + barBgStyle.hover.background = GUIHelper.ConvertTextureToColor(new Color(0, 0, 0, 0.1f)); + barBgStyle.focused.background = GUIHelper.ConvertTextureToColor(Color.clear); + barBgStyle.active.background = null; + return barBgStyle; + } + } + + internal static GUIStyle ChildBarStyle { + get { + if (barChildStyle == null) { + barChildStyle = new GUIStyle(BarStyle); + barChildStyle.padding = new RectOffset(43, barChildStyle.padding.right, barChildStyle.padding.top, barChildStyle.padding.bottom); + } + return barChildStyle; + } + } + + internal static GUIStyle FoldoutBarStyle { + get { + if (barFoldoutStyle == null) { + barFoldoutStyle = new GUIStyle(BarStyle); + barFoldoutStyle.padding = new RectOffset(23, barFoldoutStyle.padding.right, barFoldoutStyle.padding.top, barFoldoutStyle.padding.bottom); + } + return barFoldoutStyle; + } + } + + public static GUIStyle TimestampStyle { + get { + if (timestampStyle == null) { + timestampStyle = new GUIStyle(GUI.skin.label); + } + if (IsDarkMode) { + timestampStyle.normal.textColor = new Color(0.5f, 0.5f, 0.5f); + } else { + timestampStyle.normal.textColor = new Color(0.5f, 0.5f, 0.5f); + } + timestampStyle.hover = timestampStyle.normal; + return timestampStyle; + } + } + + internal static GUIStyle ClickableLabelBoldStyle { + get { + if (clickableLabelBoldStyle == null) { + clickableLabelBoldStyle = new GUIStyle(LabelStyle); + clickableLabelBoldStyle.fontStyle = FontStyle.Bold; + clickableLabelBoldStyle.fontSize = 14; + clickableLabelBoldStyle.margin.left = 17; + clickableLabelBoldStyle.active.textColor = clickableLabelBoldStyle.normal.textColor; + } + return clickableLabelBoldStyle; + } + } + + internal static GUIStyle EmptyListText { + get { + if (_emptyListText == null) { + _emptyListText = new GUIStyle(); + _emptyListText.fontSize = 11; + _emptyListText.padding.left = 15; + _emptyListText.padding.top = 10; + _emptyListText.alignment = TextAnchor.MiddleCenter; + _emptyListText.normal.textColor = Color.gray; + } + + return _emptyListText; + } + } + + internal static GUIStyle StacktraceTextAreaStyle { + get { + if (_stacktraceTextAreaStyle == null) { + _stacktraceTextAreaStyle = new GUIStyle(EditorStyles.textArea); + _stacktraceTextAreaStyle.border = new RectOffset(0, 0, 0, 0); + } + return _stacktraceTextAreaStyle; + } + } + + internal static GUIStyle EntryBoxStyle { + get { + if (_entryBoxStyle == null) { + _entryBoxStyle = new GUIStyle(); + _entryBoxStyle.margin.left = 30; + } + return _entryBoxStyle; + } + } + + internal static GUIStyle ChildEntryBoxStyle { + get { + if (_childEntryBoxStyle == null) { + _childEntryBoxStyle = new GUIStyle(); + _childEntryBoxStyle.margin.left = 45; + } + return _childEntryBoxStyle; + } + } + + internal static GUIStyle CustomFoldoutStyle { + get { + if (_customFoldoutStyle == null) { + _customFoldoutStyle = new GUIStyle(EditorStyles.foldout); + _customFoldoutStyle.margin.top = 4; + _customFoldoutStyle.margin.left = 0; + _customFoldoutStyle.padding.left = 0; + _customFoldoutStyle.fixedWidth = 100; + } + return _customFoldoutStyle; + } + } + + internal static GUIStyle RemoveIconStyle { + get { + if (_removeIconStyle == null) { + _removeIconStyle = new GUIStyle(); + _removeIconStyle.margin.top = 5; + _removeIconStyle.fixedWidth = 17; + _removeIconStyle.fixedHeight = 17; + } + return _removeIconStyle; + } + } + + internal static GUIStyle UpgradeLicenseButtonStyle { + get { + if (upgradeLicenseButtonStyle == null) { + upgradeLicenseButtonStyle = new GUIStyle(GUI.skin.button); + upgradeLicenseButtonStyle.padding = new RectOffset(5, 5, 0, 0); + } + return upgradeLicenseButtonStyle; + } + } + + internal static GUIStyle UpgradeLicenseButtonOverlayStyle { + get { + if (upgradeLicenseButtonOverlayStyle == null) { + upgradeLicenseButtonOverlayStyle = new GUIStyle(UpgradeLicenseButtonStyle); + } + return upgradeLicenseButtonOverlayStyle; + } + } + + internal static GUIStyle UpgradeButtonStyle { + get { + if (upgradeButtonStyle == null) { + upgradeButtonStyle = new GUIStyle(EditorStyles.miniButton); + upgradeButtonStyle.fontStyle = FontStyle.Bold; + upgradeButtonStyle.fontSize = 14; + upgradeButtonStyle.fixedHeight = 24; + } + return upgradeButtonStyle; + } + } + + internal static GUIStyle HideButtonStyle { + get { + if (hideButtonStyle == null) { + hideButtonStyle = new GUIStyle(GUI.skin.button); + } + return hideButtonStyle; + } + } + + internal static GUIStyle HelpTabButton { + get { + if (helpTabButton == null) { + helpTabButton = new GUIStyle(GUI.skin.button); + helpTabButton.alignment = TextAnchor.MiddleLeft; + helpTabButton.padding.left = 10; + } + return helpTabButton; + } + } + + internal static GUIStyle IndicationHelpBox { + get { + if (indicationHelpBox == null) { + indicationHelpBox = new GUIStyle(EditorStyles.helpBox); + indicationHelpBox.margin.right = 0; + indicationHelpBox.margin.left = 0; + } + return indicationHelpBox; + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs.meta b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs.meta new file mode 100644 index 0000000..9c7385a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c06a986e9e8c3874f9578f0002ff3a2d +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/Window/Styles/HotReloadWindowStyles.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/HotReload.code-workspace b/Packages/com.singularitygroup.hotreload/HotReload.code-workspace new file mode 100644 index 0000000..c6653fc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/HotReload.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../../../script" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/HotReload.code-workspace.meta b/Packages/com.singularitygroup.hotreload/HotReload.code-workspace.meta new file mode 100644 index 0000000..f1d71a4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/HotReload.code-workspace.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: c5fb35f04def51646a584aa23b72d056 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/HotReload.code-workspace + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/LICENSE.md b/Packages/com.singularitygroup.hotreload/LICENSE.md new file mode 100644 index 0000000..b9eb62e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/LICENSE.md @@ -0,0 +1,45 @@ +End User License Agreement (“EULA”) for Hot Reload for Unity (“Software”) +Please read this End-User License Agreement ("Agreement") carefully before purchasing, downloading, or using Hot Reload for Unity ("Software"). + +By purchasing, downloading or using the Software, you, the individual or entity (“End-User”), agree to be bound by this EULA as well as by our Terms and Conditions. + +If End-User does not agree to the terms of this Agreement, do not purchase, download or use the Software. + +The subject matter of this EULA is the licensing of the Software to End-User. The Software is licensed, not sold. + +License + +The Naughty Cult Ltd. (“Licensor”) grants End-User a revocable, non-exclusive, worldwide, non-transferable, limited license to download, install and use the Software for personal and commercial purposes in accordance with the terms of this Agreement and the terms set out in the Terms and Conditions. + +The Software is owned and copyrighted by The Naughty Cult Ltd.. Your license confers no title or ownership in the Software and should not be construed as a sale of any right in the Software. + +The Software is protected by copyright law and international treaty provisions. You acknowledge that no ownership of the intellectual property in the Software is transferred to you. You further acknowledge that The Naughty Cult Ltd. retains full ownership rights to the Software, and you will not acquire any rights to the Software except as outlined in this license. You agree that any copies of the Software will include the same proprietary notices found on and within the original Software. + +End-User's Rights and Obligations + +End-User may use the licensed Software only for its intended purpose. End-User may not modify, reproduce, distribute, sublicense, rent, lease or lend the Software. +Each active license allows End-User to install and use the Software on a maximum of two devices associated with one specific Unity seat. End-User may not share the Software or the license key with any third party. + +You may not modify the Software or disable any licensing or control features of the Software without express permission from the Licensor. You agree that you will not attempt to reverse compile, modify, translate, or disassemble the Software in whole or in part. + +Once End-User's active license is terminated, End-User will not receive any new updates to the Software, and may not download, install, integrate or otherwise use versions of the Software released at any time hereafter, unless a license is activated. + +Termination +This EULA will terminate automatically if End-User fails to comply with any of the terms and conditions of this Agreement. In such event, End-User must immediately stop using the Software and destroy all copies of the Software in End-User's possession. + +Governing Law +This EULA shall be governed by the laws of the country in which the Licensor is headquartered without regard to its conflict of law provisions. We reserve the right to terminate or suspend your account, without notice or liability, for any reason, including breach of the Terms and Conditions and/or EULA. + +Limitation of Liability +The Naughty Cult Ltd. and its affiliates shall not be held liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from your use of or inability to use the Service, any conduct or content of any third party on the Service, any content obtained from the Service, or unauthorized access or alteration of your transmissions or content. This limitation applies regardless of whether the damages are based on warranty, contract, tort (including negligence), or any other legal theory, and even if we have been advised of the possibility of such damages. + + +Disclaimer of Warranties + +The Service is provided on an "as is" and "as available" basis without any warranties of any kind, either express or implied. We do not warrant that the Service will be uninterrupted or error-free, or that any defects will be corrected. We also do not guarantee that the Service will meet your requirements. + +Waiver and Severability +Our failure to enforce any right or provision of this EULA will not be deemed a waiver of such right or provision. In the event that any provision of these EULA is held to be invalid or unenforceable, the remaining provisions will remain in full force and effect. + +Entire Agreement +This EULA constitutes the entire agreement between End-User and Licensor regarding the use of the Software and supersedes all prior agreements and understandings, whether written or oral. diff --git a/Packages/com.singularitygroup.hotreload/LICENSE.md.meta b/Packages/com.singularitygroup.hotreload/LICENSE.md.meta new file mode 100644 index 0000000..e8d5657 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/LICENSE.md.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0f0ed454ae8a66041bea966cdcee0f2e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/LICENSE.md + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/README.md b/Packages/com.singularitygroup.hotreload/README.md new file mode 100644 index 0000000..1c30967 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/README.md @@ -0,0 +1,9 @@ +![HotReloadLogo](https://hotreload.net/logo.png?w=256&q=75) + +# Hot Reload for Unity + +Edit **any C# function** and get immediate updates in your game. Hot Reload works with your existing project, no code changes required. + +Install instructions on https://hotreload.net/ + +![Modify2dJumpingGameDemo](https://hot-reload-assets.s3.amazonaws.com/assets/hotreload_jump_demo.gif) diff --git a/Packages/com.singularitygroup.hotreload/README.md.meta b/Packages/com.singularitygroup.hotreload/README.md.meta new file mode 100644 index 0000000..4a80df0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/README.md.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bdb53603710c4ae3b491b7885e5ff702 +timeCreated: 1674514875 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/README.md + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime.meta b/Packages/com.singularitygroup.hotreload/Runtime.meta new file mode 100644 index 0000000..2b0c282 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8026562867072c3409c904654ec3c17f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs b/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs new file mode 100644 index 0000000..77618a6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using UnityEngine; + +namespace SingularityGroup.HotReload { + class AppCallbackListener : MonoBehaviour { + /// + /// Reliable on Android and in the editor. + /// + /// + /// On iOS, OnApplicationPause is not called at expected moments + /// if the app has some background modes enabled in PlayerSettings -Troy. + /// + public static event Action onApplicationPause; + + /// + /// Reliable on Android, iOS and in the editor. + /// + public static event Action onApplicationFocus; + + static AppCallbackListener instance; + public static AppCallbackListener I => instance; + + // Must be called early from Unity main thread (before any usages of the singleton I). + public static AppCallbackListener Init() { + if(instance) return instance; + var go = new GameObject("AppCallbackListener"); + go.hideFlags |= HideFlags.HideInHierarchy; + DontDestroyOnLoad(go); + return instance = go.AddComponent(); + } + + public bool Paused { get; private set; } = false; + + public void DelayedQuit(float seconds) { + StartCoroutine(delayedQuitRoutine(seconds)); + } + + IEnumerator delayedQuitRoutine(float seconds) { + yield return new WaitForSeconds(seconds); + Application.Quit(); + } + + void OnApplicationPause(bool paused) { + Paused = paused; + onApplicationPause?.Invoke(paused); + } + + void OnApplicationFocus(bool playing) { + onApplicationFocus?.Invoke(playing); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs.meta new file mode 100644 index 0000000..0d7b6ec --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a989a17330b04c6fb8f91aa41ac14471 +timeCreated: 1674216227 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs new file mode 100644 index 0000000..392ad0f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.IO; +using JetBrains.Annotations; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEngine; +using UnityEngine.Serialization; + +namespace SingularityGroup.HotReload { + /// + /// Information about the Unity Player build. + /// + /// + /// + /// This info is used by the HotReload Server to compile your project in the same way that the Unity Player build was compiled.
+ /// For example, when building for Android, Unity sets a bunch of define symbols like UNITY_ANDROID. + ///
+ /// + /// Information that changes between builds is generated at build-time and put in StreamingAssets/.
+ /// This approach means that builds do not need to modify a project file (making file dirty in git). For example, + /// whenever user makes a mono build, the CommitHash changes and we need to regenerate the BuildInfo. + ///
+ ///
+ [Serializable] + class BuildInfo { + /// + /// Uniquely identifies the Unity project. + /// + /// + /// Used on-device to check if Hot Reload server is compatible with the Unity project (same project).
+ /// When your computer has multiple Unity projects open, each project should provide a different value.
+ /// This identifier must also be the same between two different computers that are collaborating on the same project. + /// + /// + /// Edge-case: when a user copy pastes an entire Unity project and has both open at once, + /// then it's fine for this identifier to be the same. + /// + ///
+ public string projectIdentifier; + + /// + /// Git commit hash + /// + /// + /// Used to detect that your code is different to when the build was made. + /// + public string commitHash; + + /// + /// List of define symbols that were active when this build was made. + /// + /// + /// Separate the symbols with a semi-colon character ';' + /// + public string defineSymbols; + + /// + /// A regex of C# project names (*.csproj) to be omitted from compilation. + /// + /// + /// "MyTests|MyEditorAssembly" + /// + [FormerlySerializedAs("projectExclusionRegex")] + public string projectOmissionRegex; + + /// + /// The computer that made the Android (or Standalone etc) build.
+ /// The hostname (ip address) where Hot Reload server would be listening. + ///
+ public string buildMachineHostName; + + /// + /// The computer that made the Android (or Standalone etc) build.
+ /// The port where Hot Reload server would be listening. + ///
+ public int buildMachinePort; + + /// + /// Selected build target in Unity Editor. + /// + public string activeBuildTarget; + + /// + /// Used to pass in the origin onto the phone which is used to identify the correct server. + /// + public string buildMachineRequestOrigin; + + /// + /// Used to define which language the package is translated to + /// + public string locale; + + [JsonIgnore] + public HashSet DefineSymbolsAsHashSet { + get { + var symbols = defineSymbols.Trim().Split(';'); + // split on an empty string produces 1 empty string + if (symbols.Length == 1 && symbols[0] == string.Empty) { + return new HashSet(); + } + return new HashSet(symbols); + } + } + + [JsonIgnore] + public PatchServerInfo BuildMachineServer { + get { + if (buildMachineHostName == null || buildMachinePort == 0) { + return null; + } + return new PatchServerInfo(buildMachineHostName, buildMachinePort, commitHash, null, customRequestOrigin: buildMachineRequestOrigin); + } + } + + public string ToJson() { + return JsonConvert.SerializeObject(this); + } + + [CanBeNull] + public static BuildInfo FromJson(string json) { + if (string.IsNullOrEmpty(json)) { + return null; + } + return JsonConvert.DeserializeObject(json); + } + + /// + /// Path to read/write the json file to. + /// + /// A filepath that is inside the player build + public static string GetStoredPath() { + return Path.Combine(Application.streamingAssetsPath, GetStoredName()); + } + + public static string GetStoredName() { + return "HotReload_BuildInfo.json"; + } + + /// True if the commit hashes are definately different, otherwise False + public bool IsDifferentCommit(string remoteCommit) { + if (commitHash == PatchServerInfo.UnknownCommitHash) { + return false; + } + + return !SameCommit(commitHash, remoteCommit); + } + + /// + /// Checks whether the commits are equivalent. + /// + /// + /// + /// False if the commit hashes are definately different, otherwise True + public static bool SameCommit(string commitA, string commitB) { + if (commitA == null) { + // unknown commit hash, so approve anything + return true; + } + + if (commitA.Length == commitB.Length) { + return commitA == commitB; + } else if (commitA.Length >= 6 && commitB.Length >= 6) { + // depending on OS, the git log pretty output has different length (7 or 8 chars) + // if the longer hash starts with the shorter hash, return true + // Assumption: commits have different length. + var longer = commitA.Length > commitB.Length ? commitA : commitB; + var shorter = commitA.Length > commitB.Length ? commitB : commitA; + + return longer.StartsWith(shorter); + } + return false; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs.meta new file mode 100644 index 0000000..64478fd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 39bb7d4cd9324f31b1882354b1cde762 +timeCreated: 1673776105 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Burst.meta b/Packages/com.singularitygroup.hotreload/Runtime/Burst.meta new file mode 100644 index 0000000..f58389b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Burst.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d10d24dc13744197a80f50ac50f5d1a1 +timeCreated: 1675449699 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs b/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs new file mode 100644 index 0000000..a0debd1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs @@ -0,0 +1,25 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Reflection; +using SingularityGroup.HotReload.DTO; + +namespace SingularityGroup.HotReload.Burst { + public static class JobHotReloadUtility { + public static void HotReloadBurstCompiledJobs(SUnityJob jobData, Type proxyJobType) { + JobPatchUtility.PatchBurstCompiledJobs(jobData, proxyJobType, unityMajorVersion: + #if UNITY_2022_2_OR_NEWER + 2022 + #elif UNITY_2021_3_OR_NEWER + 2021 + #elif UNITY_2020_3_OR_NEWER + 2020 + #elif UNITY_2019_4_OR_NEWER + 2019 + #else + 2018 + #endif + ); + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs.meta new file mode 100644 index 0000000..887d007 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b9980b40e3ff447b94e71de238a37fb7 +timeCreated: 1676825622 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs new file mode 100644 index 0000000..995d1fc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace SingularityGroup.HotReload { + static class BurstChecker { + //Use names instead of the types directly for compat with older unity versions + const string whitelistAttrName = "BurstCompileAttribute"; + const string blacklistAttrName = "BurstDiscardAttribute"; + + public static bool IsBurstCompiled(MethodBase method) { + //blacklist has precedence over whitelist + if(HasAttr(method.GetCustomAttributes(), blacklistAttrName)) { + return false; + } + if(HasAttr(method.GetCustomAttributes(), whitelistAttrName)) { + return true; + } + //Static methods inside a [BurstCompile] type are not burst compiled by default + if(method.DeclaringType == null || method.IsStatic) { + return false; + } + if(HasAttr(method.DeclaringType.GetCustomAttributes(), whitelistAttrName)) { + return true; + } + //No matching attributes + return false; + } + + static bool HasAttr(IEnumerable attributes, string name) { + foreach (var attr in attributes) { + if(attr.GetType().Name == name) { + return true; + } + } + return false; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs.meta new file mode 100644 index 0000000..075f40c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 20dfd902e9fc4485aeef90b9add39c0a +timeCreated: 1675404225 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs new file mode 100644 index 0000000..7f7999d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs @@ -0,0 +1,770 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Localization; +using JetBrains.Annotations; +using SingularityGroup.HotReload.Burst; +using SingularityGroup.HotReload.HarmonyLib; +using SingularityGroup.HotReload.JsonConverters; +using SingularityGroup.HotReload.MonoMod.Utils; +using SingularityGroup.HotReload.Newtonsoft.Json; +using SingularityGroup.HotReload.RuntimeDependencies; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditorInternal; +#endif +using UnityEngine; +using UnityEngine.SceneManagement; + +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.Editor")] + +namespace SingularityGroup.HotReload { + class RegisterPatchesResult { + // note: doesn't include removals and method definition changes (e.g. renames) + public readonly List patchedMethods = new List(); + public List addedFields = new List(); + public readonly List patchedSMethods = new List(); + public bool inspectorModified; + public bool inspectorFieldAdded; + public readonly List> patchFailures = new List>(); + public readonly List patchExceptions = new List(); + } + + class FieldHandler { + public readonly Func storeField; + public readonly Action registerInspectorFieldAttributes; + public readonly Func hideField; + + public FieldHandler(Func storeField, Func hideField, Action registerInspectorFieldAttributes) { + this.storeField = storeField; + this.hideField = hideField; + this.registerInspectorFieldAttributes = registerInspectorFieldAttributes; + } + } + + class CodePatcher { + public static readonly CodePatcher I = new CodePatcher(); + /// Tag for use in Debug.Log. + public const string TAG = "HotReload"; + + internal int PatchesApplied { get; private set; } + string PersistencePath {get;} + + List pendingPatches; + readonly List patchHistory; + readonly HashSet seenResponses = new HashSet(); + string[] assemblySearchPaths; + SymbolResolver symbolResolver; + readonly string tmpDir; + public FieldHandler fieldHandler; + public bool debuggerCompatibilityEnabled; + public bool anyFailures; + + public IReadOnlyList PatchHistory => patchHistory; + + CodePatcher() { + pendingPatches = new List(); + patchHistory = new List(); + if(UnityHelper.IsEditor) { + tmpDir = PackageConst.LibraryCachePath; + } else { + tmpDir = UnityHelper.TemporaryCachePath; + } + if(!UnityHelper.IsEditor) { + PersistencePath = Path.Combine(UnityHelper.PersistentDataPath, "HotReload", "patches.json"); + try { + LoadPatches(PersistencePath); + } catch(Exception ex) { + Log.Error($"{Localization.Translations.Logging.LoadingPatchesFromDiskError}\n{ex}"); + } + } else { + PersistencePath = Path.Combine(PackageConst.LibraryCachePath, "patches.json"); + } +#if UNITY_EDITOR + // Unity event methods are not assigned outside the scene. + // So we need to ensure they are added when entering play mode from edit mode + EditorApplication.playModeStateChanged += state => { + if (state != PlayModeStateChange.EnteredPlayMode) { + return; + } + foreach (var unityEventMethod in unityEventMethods) { + EnsureUnityEventMethod(unityEventMethod); + } + }; +#endif + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void InitializeUnityEvents() { + UnityEventHelper.Initialize(); + } + + + void LoadPatches(string filePath) { + PlayerLog(Localization.Translations.Logging.LoadingPatchesFromFile, filePath); + var file = new FileInfo(filePath); + if(file.Exists) { + var bytes = File.ReadAllText(filePath); + var patches = JsonConvert.DeserializeObject>(bytes); + PlayerLog(Localization.Translations.Logging.LoadedPatchesFromDisk, patches.Count.ToString()); + foreach (var patch in patches) { + RegisterPatches(patch, persist: false); + } + } + } + + + internal IReadOnlyList PendingPatches => pendingPatches; + internal SymbolResolver SymbolResolver => symbolResolver; + + + internal string[] GetAssemblySearchPaths() { + EnsureSymbolResolver(); + return assemblySearchPaths; + } + + internal void RegisterFailures(MethodPatchResponse patch, RegisterPatchesResult result) { + anyFailures |= patch.failures?.Length > 0 || result?.patchFailures.Count > 0 || result?.patchExceptions.Count > 0; + } + + internal RegisterPatchesResult RegisterPatches(MethodPatchResponse patches, bool persist) { + PlayerLog(Localization.Translations.Logging.RegisterPatches, string.Join("\n", patches.failures), string.Join("\n", patches.patches.SelectMany(p => p.modifiedMethods).Select(m => m.displayName))); + pendingPatches.Add(patches); + return ApplyPatches(persist); + } + + RegisterPatchesResult ApplyPatches(bool persist) { + PlayerLog(Localization.Translations.Logging.ApplyPatchesPending, pendingPatches.Count); + EnsureSymbolResolver(); + + var result = new RegisterPatchesResult(); + + try { + int count = 0; + foreach(var response in pendingPatches) { + if (seenResponses.Contains(response.id)) { + continue; + } + foreach (var patch in response.patches) { + var asm = Assembly.Load(patch.patchAssembly, patch.patchPdb); + SymbolResolver.AddAssembly(asm); + } + HandleRemovedUnityMethods(response.removedMethod); +#if UNITY_EDITOR + HandleAlteredFields(response.id, result, response.alteredFields); +#endif + // needs to come before RegisterNewFieldInitializers + RegisterNewFieldDefinitions(response); + // Note: order is important here. Reshaped fields require new field initializers to be added + // because the old initializers must override new initilaizers for existing holders. + // so that the initializer is not invoked twice + RegisterNewFieldInitializers(response); + HandleReshapedFields(response); + RemoveOldFieldInitializers(response); +#if UNITY_EDITOR + RegisterInspectorFieldAttributes(result, response); +#endif + + HandleMethodPatchResponse(response, result); + patchHistory.Add(response); + + seenResponses.Add(response.id); + count += response.patches.Length; + } + if (count > 0) { + Dispatch.OnHotReload(result.patchedMethods).Forget(); + } + } catch(Exception ex) { + Log.Warning($"{Localization.Translations.Logging.ExceptionHandlingMethodPatch}\n{ex}"); + } finally { + pendingPatches.Clear(); + } + + if(PersistencePath != null && persist) { + SaveAppliedPatches(PersistencePath).Forget(); + } + + PatchesApplied++; + return result; + } + + internal void ClearPatchedMethods() { + PatchesApplied = 0; + } + + static bool didLog; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + static void WarnOnSceneLoad() { + SceneManager.sceneLoaded += (_, __) => { + if (didLog || !UnityEventHelper.UnityMethodsAdded()) { + return; + } + Log.Warning(Localization.Translations.Logging.SceneLoadedWithNewUnityEventMethods); + didLog = true; + }; + } + + static HashSet unityEventMethods = new HashSet(); + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + static void OnSceneLoad() { + SceneManager.sceneLoaded += (_, __) => { + foreach (var unityEventMethod in unityEventMethods) { + EnsureUnityEventMethod(unityEventMethod); + } + }; + } + + static bool EnsureUnityEventMethod(MethodBase newMethod) { + try { + return UnityEventHelper.EnsureUnityEventMethod(newMethod); + } catch(Exception ex) { + Log.Warning(Localization.Translations.Logging.ExceptionEnsureUnityEventMethod, ex.GetType().Name, ex.Message); + return false; + } + } + + void HandleMethodPatchResponse(MethodPatchResponse response, RegisterPatchesResult result) { + EnsureSymbolResolver(); + + foreach(var patch in response.patches) { + try { + foreach(var sMethod in patch.newMethods) { + var newMethod = SymbolResolver.Resolve(sMethod); + + var isUnityEvent = EnsureUnityEventMethod(newMethod); + if (isUnityEvent) { + unityEventMethods.Add(newMethod); + } + + MethodUtils.DisableVisibilityChecks(newMethod); + if (!patch.patchMethods.Any(m => m.metadataToken == sMethod.metadataToken)) { + result.patchedMethods.Add(new MethodPatch(null, null, newMethod)); + result.patchedSMethods.Add(sMethod); + previousPatchMethods[newMethod] = newMethod; + newMethods.Add(newMethod); + } + } + + for (int i = 0; i < patch.modifiedMethods.Length; i++) { + var sOriginalMethod = patch.modifiedMethods[i]; + var sPatchMethod = patch.patchMethods[i]; + var err = PatchMethod(response.id, sOriginalMethod: sOriginalMethod, sPatchMethod: sPatchMethod, containsBurstJobs: patch.unityJobs.Length > 0, patchesResult: result); + if (!string.IsNullOrEmpty(err)) { + result.patchFailures.Add(Tuple.Create(sOriginalMethod, err)); + } + } + foreach (var job in patch.unityJobs) { + var type = SymbolResolver.Resolve(new SType(patch.assemblyName, job.jobKind.ToString(), job.metadataToken)); + JobHotReloadUtility.HotReloadBurstCompiledJobs(job, type); + } +#if UNITY_EDITOR + HandleNewFields(patch.patchId, result, patch.newFields); +#endif + } catch (Exception ex) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Exception), new EditorExtraData { + { StatKey.PatchId, patch.patchId }, + { StatKey.Detailed_Exception, ex.ToString() }, + }).Forget(); + result.patchExceptions.Add($"{Localization.Translations.Logging.ExceptionApplyingPatch}\nException: {ex}"); + } + } + } + + void HandleRemovedUnityMethods(SMethod[] removedMethods) { + if (removedMethods == null) { + return; + } + foreach(var sMethod in removedMethods) { + try { + var oldMethod = SymbolResolver.Resolve(sMethod); + UnityEventHelper.RemoveUnityEventMethod(oldMethod); + unityEventMethods.Remove(oldMethod); + } catch (SymbolResolvingFailedException) { + // ignore, not a unity event method if can't resolve + } catch(Exception ex) { + Log.Warning(Localization.Translations.Logging.ExceptionRemoveUnityEventMethod, ex.GetType().Name, ex.Message); + } + } + } + + // Important: must come before applying any patches + void RegisterNewFieldInitializers(MethodPatchResponse resp) { + for (var i = 0; i < resp.addedFieldInitializerFields.Length; i++) { + var sField = resp.addedFieldInitializerFields[i]; + var sMethod = resp.addedFieldInitializerInitializers[i]; + try { + var declaringType = SymbolResolver.Resolve(sField.declaringType); + var method = SymbolResolver.Resolve(sMethod); + if (!(method is MethodInfo initializer)) { + Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringInitializerInvalidMethod, sField.fieldName, sField.declaringType.typeName)); + continue; + } + // We infer if the field is static by the number of parameters the method has + // because sField is old field + var isStatic = initializer.GetParameters().Length == 0; + MethodUtils.DisableVisibilityChecks(initializer); + // Initializer return type is used in place of fieldType because latter might be point to old field if the type changed + FieldInitializerRegister.RegisterInitializer(declaringType, sField.fieldName, initializer.ReturnType, initializer, isStatic); + + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.RegisterFieldInitializer), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringInitializerException, sField.fieldName, sField.declaringType.typeName, e.Message)); + } + } + } + + void RegisterNewFieldDefinitions(MethodPatchResponse resp) { + foreach (var sField in resp.newFieldDefinitions) { + try { + var declaringType = SymbolResolver.Resolve(sField.declaringType); + var fieldType = SymbolResolver.Resolve(sField).FieldType; + FieldResolver.RegisterFieldType(declaringType, sField.fieldName, fieldType); + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.RegisterFieldDefinition), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedRegisteringNewFieldDefinitions, sField.fieldName, sField.declaringType.typeName, e.Message)); + } + } + } + + // Important: must come before applying any patches + // Note: server might decide not to report removed field initializer at all if it can handle it + void RemoveOldFieldInitializers(MethodPatchResponse resp) { + foreach (var sField in resp.removedFieldInitializers) { + try { + var declaringType = SymbolResolver.Resolve(sField.declaringType); + var fieldType = SymbolResolver.Resolve(sField.declaringType); + FieldInitializerRegister.UnregisterInitializer(declaringType, sField.fieldName, fieldType, sField.isStatic); + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.UnregisterFieldInitializer), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedRemovingInitializer, sField.fieldName, sField.declaringType.typeName, e.Message)); + } + } + } + + // Important: must come before applying any patches + // Should also come after RegisterNewFieldInitializers so that new initializers are not invoked for existing objects + internal void HandleReshapedFields(MethodPatchResponse resp) { + foreach(var patch in resp.patches) { + var removedReshapedFields = patch.deletedFields; + var renamedReshapedFieldsFrom = patch.renamedFieldsFrom; + var renamedReshapedFieldsTo = patch.renamedFieldsTo; + + foreach (var f in removedReshapedFields) { + try { + var declaringType = SymbolResolver.Resolve(f.declaringType); + var fieldType = SymbolResolver.Resolve(f).FieldType; + FieldResolver.ClearHolders(declaringType, f.isStatic, f.fieldName, fieldType); + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.ClearHolders), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedRemovingFieldValue, f.fieldName, f.declaringType.typeName, e.Message)); + } + } + for (var i = 0; i < renamedReshapedFieldsFrom.Length; i++) { + var fromField = renamedReshapedFieldsFrom[i]; + var toField = renamedReshapedFieldsTo[i]; + try { + var declaringType = SymbolResolver.Resolve(fromField.declaringType); + var fieldType = SymbolResolver.Resolve(fromField).FieldType; + var toFieldType = SymbolResolver.Resolve(toField).FieldType; + if (!AreSTypesCompatible(fromField.declaringType, toField.declaringType) + || fieldType != toFieldType + || fromField.isStatic != toField.isStatic + ) { + FieldResolver.ClearHolders(declaringType, fromField.isStatic, fromField.fieldName, fieldType); + continue; + } + FieldResolver.MoveHolders(declaringType, fromField.fieldName, toField.fieldName, fieldType, fromField.isStatic); + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MoveHolders), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(Localization.Translations.Logging.FailedMovingFieldValue, fromField, toField, toField.declaringType.typeName, e.Message); + } + } + } + } + + internal bool AreSTypesCompatible(SType one, SType two) { + if (one.isGenericParameter != two.isGenericParameter) { + return false; + } + if (one.metadataToken != two.metadataToken) { + return false; + } + if (one.assemblyName != two.assemblyName) { + return false; + } + if (one.genericParameterPosition != two.genericParameterPosition) { + return false; + } + if (one.typeName != two.typeName) { + return false; + } + return true; + } + +#if UNITY_EDITOR + internal void RegisterInspectorFieldAttributes(RegisterPatchesResult result, MethodPatchResponse resp) { + foreach (var patch in resp.patches) { + var propertyAttributesFieldOriginal = patch.propertyAttributesFieldOriginal ?? Array.Empty(); + var propertyAttributesFieldUpdated = patch.propertyAttributesFieldUpdated ?? Array.Empty(); + for (var i = 0; i < propertyAttributesFieldOriginal.Length; i++) { + var original = propertyAttributesFieldOriginal[i]; + var updated = propertyAttributesFieldUpdated[i]; + try { + var declaringType = SymbolResolver.Resolve(original.declaringType); + var originalField = SymbolResolver.Resolve(original); + var updatedField = SymbolResolver.Resolve(updated); + fieldHandler?.registerInspectorFieldAttributes?.Invoke(declaringType, originalField, updatedField); + result.inspectorModified = true; + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MoveHolders), new EditorExtraData { + { StatKey.PatchId, resp.id }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedUpdatingFieldAttributes, original.fieldName, original.declaringType.typeName, e.Message)); + } + } + } + } + + internal void HandleNewFields(string patchId, RegisterPatchesResult result, SField[] sFields) { + foreach (var sField in sFields) { + if (!sField.serializable) { + continue; + } + try { + var declaringType = SymbolResolver.Resolve(sField.declaringType); + var field = SymbolResolver.Resolve(sField); + result.inspectorFieldAdded = fieldHandler?.storeField?.Invoke(declaringType, field) ?? false; + result.inspectorModified = true; + } catch (Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.AddInspectorField), new EditorExtraData { + { StatKey.PatchId, patchId }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedAddingFieldToInspector, sField.fieldName, sField.declaringType.typeName, e.Message)); + } + } + result.addedFields.AddRange(sFields); + } + + // IMPORTANT: must come before HandleNewFields. Might contain new fields which we don't want to hide + internal void HandleAlteredFields(string patchId, RegisterPatchesResult result, SField[] alteredFields) { + if (alteredFields == null) { + return; + } + bool alteredFieldHidden = false; + foreach(var sField in alteredFields) { + try { + var declaringType = SymbolResolver.Resolve(sField.declaringType); + if (fieldHandler?.hideField?.Invoke(declaringType, sField.fieldName) == true) { + alteredFieldHidden = true; + } + } catch(Exception e) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.HideInspectorField), new EditorExtraData { + { StatKey.PatchId, patchId }, + { StatKey.Detailed_Exception, e.ToString() }, + }).Forget(); + Log.Warning(string.Format(Localization.Translations.Logging.FailedHidingFieldFromInspector, sField.fieldName, sField.declaringType.typeName, e.Message)); + } + } + if (alteredFieldHidden) { + result.inspectorModified = true; + } + } +#endif + + Dictionary previousPatchMethods = new Dictionary(); + public IEnumerable OriginalPatchMethods => previousPatchMethods.Keys; + List newMethods = new List(); + + string PatchMethod(string patchId, SMethod sOriginalMethod, SMethod sPatchMethod, bool containsBurstJobs, RegisterPatchesResult patchesResult) { + try { + var patchMethod = SymbolResolver.Resolve(sPatchMethod); + var start = DateTime.UtcNow; + var state = TryResolveMethod(sOriginalMethod, patchMethod); + if (Debugger.IsAttached && !debuggerCompatibilityEnabled) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.DebuggerAttached), new EditorExtraData { + { StatKey.PatchId, patchId }, + }).Forget(); + return Localization.Translations.Logging.DebuggerAttachedNotAllowed; + } + + if (DateTime.UtcNow - start > TimeSpan.FromMilliseconds(500)) { + Log.Info(Localization.Translations.Logging.HotReloadApplyTook, (DateTime.UtcNow - start).TotalMilliseconds); + } + + if(state.match == null) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.MethodMismatch), new EditorExtraData { + { StatKey.PatchId, patchId }, + }).Forget(); + return string.Format(Localization.Translations.Logging.MethodMismatch, sOriginalMethod.simpleName, patchMethod.Name); + } + + PlayerLog(Localization.Translations.Logging.DetourMethod, sOriginalMethod.metadataToken, patchMethod.Name, state.offset); + DetourResult result; + DetourApi.DetourMethod(state.match, patchMethod, out result); + if (result.success) { + // previous method is either original method or the last patch method + MethodBase previousMethod; + if (!previousPatchMethods.TryGetValue(state.match, out previousMethod)) { + previousMethod = state.match; + } + MethodBase originalMethod = state.match; + if (newMethods.Contains(state.match)) { + // for function added at runtime the original method should be null + originalMethod = null; + } + patchesResult.patchedMethods.Add(new MethodPatch(originalMethod, previousMethod, patchMethod)); + patchesResult.patchedSMethods.Add(sOriginalMethod); + previousPatchMethods[state.match] = patchMethod; + try { + Dispatch.OnHotReloadLocal(state.match, patchMethod); + } catch { + // best effort + } + return null; + } else { + if(result.exception is InvalidProgramException && containsBurstJobs) { + //ignore. The method is likely burst compiled and can't be patched + return null; + } else { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Failure), new EditorExtraData { + { StatKey.PatchId, patchId }, + { StatKey.Detailed_Exception, result.exception.ToString() }, + }).Forget(); + return HandleMethodPatchFailure(sOriginalMethod, result.exception); + } + } + } catch(Exception ex) { + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Error, StatFeature.Patching, StatEventType.Exception), new EditorExtraData { + { StatKey.PatchId, patchId }, + { StatKey.Detailed_Exception, ex.ToString() }, + }).Forget(); + return HandleMethodPatchFailure(sOriginalMethod, ex); + } + } + + struct ResolveMethodState { + public readonly SMethod originalMethod; + public readonly int offset; + public readonly bool tryLowerTokens; + public readonly bool tryHigherTokens; + public readonly MethodBase match; + public ResolveMethodState(SMethod originalMethod, int offset, bool tryLowerTokens, bool tryHigherTokens, MethodBase match) { + this.originalMethod = originalMethod; + this.offset = offset; + this.tryLowerTokens = tryLowerTokens; + this.tryHigherTokens = tryHigherTokens; + this.match = match; + } + + public ResolveMethodState With(bool? tryLowerTokens = null, bool? tryHigherTokens = null, MethodBase match = null, int? offset = null) { + return new ResolveMethodState( + originalMethod, + offset ?? this.offset, + tryLowerTokens ?? this.tryLowerTokens, + tryHigherTokens ?? this.tryHigherTokens, + match ?? this.match); + } + } + + struct ResolveMethodResult { + public readonly MethodBase resolvedMethod; + public readonly bool tokenOutOfRange; + public ResolveMethodResult(MethodBase resolvedMethod, bool tokenOutOfRange) { + this.resolvedMethod = resolvedMethod; + this.tokenOutOfRange = tokenOutOfRange; + } + } + + ResolveMethodState TryResolveMethod(SMethod originalMethod, MethodBase patchMethod) { + var state = new ResolveMethodState(originalMethod, offset: 0, tryLowerTokens: true, tryHigherTokens: true, match: null); + var result = TryResolveMethodCore(state.originalMethod, patchMethod, 0); + if(result.resolvedMethod != null) { + return state.With(match: result.resolvedMethod); + } + state = state.With(offset: 1); + const int tries = 100000; + while(state.offset <= tries && (state.tryHigherTokens || state.tryLowerTokens)) { + if(state.tryHigherTokens) { + result = TryResolveMethodCore(originalMethod, patchMethod, state.offset); + if(result.resolvedMethod != null) { + return state.With(match: result.resolvedMethod); + } else if(result.tokenOutOfRange) { + state = state.With(tryHigherTokens: false); + } + } + if(state.tryLowerTokens) { + result = TryResolveMethodCore(originalMethod, patchMethod, -state.offset); + if(result.resolvedMethod != null) { + return state.With(match: result.resolvedMethod); + } else if(result.tokenOutOfRange) { + state = state.With(tryLowerTokens: false); + } + } + state = state.With(offset: state.offset + 1); + } + return state; + } + + + ResolveMethodResult TryResolveMethodCore(SMethod methodToResolve, MethodBase patchMethod, int offset) { + bool tokenOutOfRange = false; + MethodBase resolvedMethod = null; + try { + resolvedMethod = TryGetMethodBaseWithRelativeToken(methodToResolve, offset); + var err = MethodCompatiblity.CheckCompatibility(resolvedMethod, patchMethod); + if(err != null) { + // if (resolvedMethod.Name == patchMethod.Name) { + // Log.Info(err); + // } + resolvedMethod = null; + } + } catch (SymbolResolvingFailedException ex) when(ex.InnerException is ArgumentOutOfRangeException) { + tokenOutOfRange = true; + } catch (ArgumentOutOfRangeException) { + tokenOutOfRange = true; + } + return new ResolveMethodResult(resolvedMethod, tokenOutOfRange); + } + + MethodBase TryGetMethodBaseWithRelativeToken(SMethod sOriginalMethod, int offset) { + return symbolResolver.Resolve(new SMethod(sOriginalMethod.assemblyName, + sOriginalMethod.displayName, + sOriginalMethod.metadataToken + offset, + sOriginalMethod.simpleName)); + } + + string HandleMethodPatchFailure(SMethod method, Exception exception) { + return string.Format(Localization.Translations.Logging.FailedToApplyPatchForMethod, method.displayName, method.assemblyName, exception); + } + + void EnsureSymbolResolver() { + if (symbolResolver == null) { + var searchPaths = new HashSet(); + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + var assembliesByName = new Dictionary>(); + for (var i = 0; i < assemblies.Length; i++) { + var name = assemblies[i].GetNameSafe(); + List list; + if (!assembliesByName.TryGetValue(name, out list)) { + assembliesByName.Add(name, list = new List()); + } + list.Add(assemblies[i]); + + if(assemblies[i].IsDynamic) continue; + + var location = assemblies[i].Location; + if(File.Exists(location)) { + searchPaths.Add(Path.GetDirectoryName(Path.GetFullPath(location))); + } + } + symbolResolver = new SymbolResolver(assembliesByName); + assemblySearchPaths = searchPaths.ToArray(); + } + } + + + //Allow one save operation at a time. + readonly SemaphoreSlim gate = new SemaphoreSlim(1); + public async Task SaveAppliedPatches(string filePath) { + await gate.WaitAsync(); + try { + await SaveAppliedPatchesNoLock(filePath); + } finally { + gate.Release(); + } + } + + async Task SaveAppliedPatchesNoLock(string filePath) { + if (filePath == null) { + throw new ArgumentNullException(nameof(filePath)); + } + filePath = Path.GetFullPath(filePath); + var dir = Path.GetDirectoryName(filePath); + if(string.IsNullOrEmpty(dir)) { + throw new ArgumentException(string.Format(Localization.Translations.Logging.InvalidPath, filePath), nameof(filePath)); + } + Directory.CreateDirectory(dir); + var history = patchHistory.ToList(); + + PlayerLog(Localization.Translations.Logging.SavingAppliedPatches, history.Count, filePath); + + await Task.Run(() => { + using (FileStream fs = File.Create(filePath)) + using (StreamWriter sw = new StreamWriter(fs)) + using (JsonWriter writer = new JsonTextWriter(sw)) { + JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings { + Converters = new List { new MethodPatchResponsesConverter() } + }); + serializer.Serialize(writer, history); + } + }); + } + + public void InitPatchesBlocked() { + if (PersistencePath == null) { + return; + } + seenResponses.Clear(); + var file = new FileInfo(PersistencePath); + if (file.Exists) { + using(var fs = new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan)) + using (StreamReader sr = new StreamReader(fs)) + using (JsonReader reader = new JsonTextReader(sr)) { + JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings { + Converters = new List { new MethodPatchResponsesConverter() } + }); + pendingPatches = serializer.Deserialize>(reader); + } + ApplyPatches(persist: false); + } + } + + public void ClearPatchesThreaded() { + if (PersistencePath == null) { + return; + } + Task.Run(() => File.Delete(PersistencePath)); + } + + + [StringFormatMethod("format")] + static void PlayerLog(string format, params object[] args) { +#if !UNITY_EDITOR + HotReload.Log.Info(format, args); +#endif //!UNITY_EDITOR + } + + class SimpleMethodComparer : IEqualityComparer { + public static readonly SimpleMethodComparer I = new SimpleMethodComparer(); + SimpleMethodComparer() { } + public bool Equals(SMethod x, SMethod y) => x.metadataToken == y.metadataToken; + public int GetHashCode(SMethod x) { + return x.metadataToken; + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs.meta new file mode 100644 index 0000000..5a81267 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b6c8477b90c3f384f8124d62a5dc6e74 +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/Runtime/CodePatcher.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo.meta new file mode 100644 index 0000000..d67b179 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 55206f9d10104e838249bf8ac177e332 +timeCreated: 1677091847 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes.meta new file mode 100644 index 0000000..a16c17b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c895e9065d763824f9211fa8054f7c2e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity new file mode 100644 index 0000000..ca6a3dd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity @@ -0,0 +1,1079 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &19295889 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 19295893} + - component: {fileID: 19295892} + - component: {fileID: 19295891} + - component: {fileID: 19295890} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &19295890 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &19295891 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 +--- !u!33 &19295892 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &19295893 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &249270788 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 249270791} + - component: {fileID: 249270790} + - component: {fileID: 249270789} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &249270789 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_Enabled: 1 +--- !u!20 &249270790 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &249270791 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &460271676 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 460271677} + - component: {fileID: 460271679} + - component: {fileID: 460271678} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &460271677 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 511172213} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -28.681885, y: -20.492146} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &460271678 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 48 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 24 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open Script +--- !u!222 &460271679 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_CullTransparentMesh: 0 +--- !u!1 &511172212 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 511172213} + - component: {fileID: 511172216} + - component: {fileID: 511172215} + - component: {fileID: 511172214} + m_Layer: 5 + m_Name: Button open script + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &511172213 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 460271677} + m_Father: {fileID: 649153321} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -256.6, y: 118} + m_SizeDelta: {x: 392.12805, y: 72.27574} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &511172214 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 511172215} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &511172215 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &511172216 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_CullTransparentMesh: 0 +--- !u!1 &649153317 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 649153321} + - component: {fileID: 649153320} + - component: {fileID: 649153319} + - component: {fileID: 649153318} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &649153318 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &649153319 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1280, y: 720} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &649153320 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 1 + m_Camera: {fileID: 249270790} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &649153321 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1537524790} + - {fileID: 1847025553} + - {fileID: 511172213} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &700195177 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 700195180} + - component: {fileID: 700195179} + - component: {fileID: 700195178} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &700195178 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &700195179 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &700195180 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &965437870 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 965437872} + - component: {fileID: 965437871} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &965437871 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965437870} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &965437872 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965437870} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1101930858 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1101930859} + - component: {fileID: 1101930861} + - component: {fileID: 1101930860} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1101930859 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1847025553} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -28.681885, y: -20.492146} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1101930860 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 48 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 24 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open Editor tab +--- !u!222 &1101930861 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_CullTransparentMesh: 0 +--- !u!1 &1537524789 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1537524790} + - component: {fileID: 1537524792} + - component: {fileID: 1537524791} + m_Layer: 5 + m_Name: InformationText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1537524790 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 649153321} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.00000095367, y: 215.4} + m_SizeDelta: {x: 861.9848, y: 122.55513} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1537524791 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 64 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Hot Reload is not running yet +--- !u!222 &1537524792 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_CullTransparentMesh: 0 +--- !u!1 &1847025552 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1847025553} + - component: {fileID: 1847025556} + - component: {fileID: 1847025555} + - component: {fileID: 1847025554} + m_Layer: 5 + m_Name: Button open editor tab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1847025553 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1101930859} + m_Father: {fileID: 649153321} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 218.9, y: 118} + m_SizeDelta: {x: 392.12805, y: 72.27574} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1847025554 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1847025555} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1847025555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1847025556 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_CullTransparentMesh: 0 +--- !u!1 &2132145875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2132145876} + - component: {fileID: 2132145877} + m_Layer: 0 + m_Name: HotReloadDemo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2132145876 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2132145875} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 18.716805, y: 53.419094, z: 92.546875} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2132145877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2132145875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5a2e4d3f095a9441688c70278068eee0, type: 3} + m_Name: + m_EditorClassIdentifier: + cube: {fileID: 19295889} + informationText: {fileID: 1537524791} + openWindowButton: {fileID: 1847025554} + openScriptButton: {fileID: 511172214} + thisScript: {fileID: 11500000, guid: 5a2e4d3f095a9441688c70278068eee0, type: 3} + myEditedField: 54 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity.meta new file mode 100644 index 0000000..5d49715 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: ae744488364b34fcf8c80218eadc721c +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity new file mode 100644 index 0000000..9ad180f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity @@ -0,0 +1,9607 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.18028378, g: 0.22571412, b: 0.30692285, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 4890085278179872738, guid: 961e97ae3d4011b47a1198a930f5c30d, type: 2} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &19295889 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 19295893} + - component: {fileID: 19295892} + - component: {fileID: 19295891} + - component: {fileID: 19295890} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &19295890 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &19295891 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &19295892 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &19295893 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 19295889} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 0.8224261, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &40618803 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 40618804} + - component: {fileID: 40618807} + - component: {fileID: 40618806} + - component: {fileID: 40618805} + m_Layer: 0 + m_Name: Cube (48) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &40618804 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 40618803} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 2.4375737, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &40618805 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 40618803} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &40618806 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 40618803} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &40618807 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 40618803} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &53988356 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 53988357} + - component: {fileID: 53988360} + - component: {fileID: 53988359} + - component: {fileID: 53988358} + m_Layer: 0 + m_Name: Cube (75) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &53988357 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53988356} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 2.427574, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &53988358 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53988356} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &53988359 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53988356} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &53988360 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53988356} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &69029314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 69029315} + - component: {fileID: 69029318} + - component: {fileID: 69029317} + - component: {fileID: 69029316} + m_Layer: 0 + m_Name: Cube (16) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &69029315 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 69029314} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: -1.6699998, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &69029316 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 69029314} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &69029317 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 69029314} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &69029318 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 69029314} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &121342030 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 121342031} + - component: {fileID: 121342034} + - component: {fileID: 121342033} + - component: {fileID: 121342032} + m_Layer: 0 + m_Name: Cube (43) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &121342031 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 121342030} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 4.92, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &121342032 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 121342030} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &121342033 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 121342030} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &121342034 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 121342030} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &127719937 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 127719938} + - component: {fileID: 127719941} + - component: {fileID: 127719940} + - component: {fileID: 127719939} + m_Layer: 0 + m_Name: Cube (67) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &127719938 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127719937} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 2.4375737, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &127719939 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127719937} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &127719940 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127719937} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &127719941 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 127719937} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &128004585 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 128004586} + - component: {fileID: 128004589} + - component: {fileID: 128004588} + - component: {fileID: 128004587} + m_Layer: 0 + m_Name: Cube (56) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &128004586 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 128004585} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 2.427574, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &128004587 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 128004585} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &128004588 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 128004585} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &128004589 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 128004585} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &132063619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 132063620} + - component: {fileID: 132063623} + - component: {fileID: 132063622} + - component: {fileID: 132063621} + m_Layer: 0 + m_Name: Cube (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &132063620 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 132063619} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 0.8224261, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &132063621 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 132063619} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &132063622 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 132063619} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &132063623 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 132063619} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &133838188 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 133838189} + - component: {fileID: 133838192} + - component: {fileID: 133838191} + - component: {fileID: 133838190} + m_Layer: 0 + m_Name: Cube (62) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &133838189 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 133838188} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 4.92, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &133838190 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 133838188} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &133838191 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 133838188} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &133838192 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 133838188} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &181686442 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 181686443} + - component: {fileID: 181686446} + - component: {fileID: 181686445} + - component: {fileID: 181686444} + m_Layer: 0 + m_Name: Cube (13) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &181686443 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 181686442} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 0.8124263, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &181686444 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 181686442} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &181686445 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 181686442} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &181686446 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 181686442} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &218081520 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 218081521} + - component: {fileID: 218081524} + - component: {fileID: 218081523} + - component: {fileID: 218081522} + m_Layer: 0 + m_Name: Cube (66) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &218081521 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 218081520} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 2.4375737, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &218081522 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 218081520} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &218081523 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 218081520} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &218081524 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 218081520} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &249270788 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 249270791} + - component: {fileID: 249270790} + - component: {fileID: 249270789} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &249270789 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_Enabled: 1 +--- !u!20 &249270790 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &249270791 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249270788} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &249919994 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 249919995} + - component: {fileID: 249919998} + - component: {fileID: 249919997} + - component: {fileID: 249919996} + m_Layer: 0 + m_Name: Cube (50) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &249919995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249919994} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 4.91, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &249919996 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249919994} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &249919997 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249919994} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &249919998 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 249919994} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &262969854 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 262969855} + - component: {fileID: 262969858} + - component: {fileID: 262969857} + - component: {fileID: 262969856} + m_Layer: 0 + m_Name: Cube (30) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &262969855 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 262969854} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 0.8124263, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &262969856 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 262969854} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &262969857 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 262969854} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &262969858 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 262969854} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &266848583 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 266848584} + - component: {fileID: 266848587} + - component: {fileID: 266848586} + - component: {fileID: 266848585} + m_Layer: 0 + m_Name: Cube (21) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &266848584 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 266848583} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 0.8224261, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &266848585 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 266848583} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &266848586 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 266848583} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &266848587 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 266848583} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &280025523 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 280025524} + - component: {fileID: 280025527} + - component: {fileID: 280025526} + - component: {fileID: 280025525} + m_Layer: 0 + m_Name: Cube (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &280025524 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280025523} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: -1.6600001, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &280025525 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280025523} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &280025526 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280025523} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &280025527 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 280025523} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &282541332 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 282541333} + - component: {fileID: 282541336} + - component: {fileID: 282541335} + - component: {fileID: 282541334} + m_Layer: 0 + m_Name: Cube (52) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &282541333 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 282541332} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 4.91, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &282541334 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 282541332} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &282541335 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 282541332} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &282541336 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 282541332} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &297017159 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 297017160} + - component: {fileID: 297017163} + - component: {fileID: 297017162} + - component: {fileID: 297017161} + m_Layer: 0 + m_Name: Cube (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &297017160 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297017159} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: -1.66, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &297017161 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297017159} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &297017162 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297017159} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &297017163 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297017159} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &297623418 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 297623419} + - component: {fileID: 297623422} + - component: {fileID: 297623421} + - component: {fileID: 297623420} + m_Layer: 0 + m_Name: Cube (76) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &297623419 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297623418} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 2.427574, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &297623420 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297623418} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &297623421 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297623418} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &297623422 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 297623418} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &315895885 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 315895886} + - component: {fileID: 315895889} + - component: {fileID: 315895888} + - component: {fileID: 315895887} + m_Layer: 0 + m_Name: Cube (72) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &315895886 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315895885} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 4.91, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &315895887 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315895885} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &315895888 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315895885} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &315895889 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315895885} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &321495839 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 321495840} + m_Layer: 0 + m_Name: Cubes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &321495840 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 321495839} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.908883, y: -0.8224261, z: -0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 19295893} + - {fileID: 323745810} + - {fileID: 582255912} + - {fileID: 1893598706} + - {fileID: 132063620} + - {fileID: 297017160} + - {fileID: 768601852} + - {fileID: 1931512540} + - {fileID: 280025524} + - {fileID: 1281233703} + - {fileID: 1927368435} + - {fileID: 2042268981} + - {fileID: 1682534256} + - {fileID: 181686443} + - {fileID: 351532142} + - {fileID: 1500025043} + - {fileID: 69029315} + - {fileID: 667369431} + - {fileID: 375087532} + - {fileID: 1124320615} + - {fileID: 927111012} + - {fileID: 266848584} + - {fileID: 2063974669} + - {fileID: 1010368909} + - {fileID: 1343033883} + - {fileID: 781926843} + - {fileID: 341472300} + - {fileID: 2136109399} + - {fileID: 677037662} + - {fileID: 1973025879} + - {fileID: 262969855} + - {fileID: 1399883246} + - {fileID: 743324179} + - {fileID: 1586797431} + - {fileID: 1123278460} + - {fileID: 928584462} + - {fileID: 1069134373} + - {fileID: 2014093030} + - {fileID: 792419466} + - {fileID: 1855770257} + - {fileID: 893274498} + - {fileID: 500782909} + - {fileID: 443249060} + - {fileID: 121342031} + - {fileID: 1130193477} + - {fileID: 523436227} + - {fileID: 1600668863} + - {fileID: 2053621922} + - {fileID: 40618804} + - {fileID: 738032838} + - {fileID: 249919995} + - {fileID: 835270569} + - {fileID: 282541333} + - {fileID: 519530843} + - {fileID: 1774902065} + - {fileID: 1019461522} + - {fileID: 128004586} + - {fileID: 1231537575} + - {fileID: 1965297415} + - {fileID: 589500252} + - {fileID: 1218495767} + - {fileID: 778191808} + - {fileID: 133838189} + - {fileID: 1983101811} + - {fileID: 387856195} + - {fileID: 2102038396} + - {fileID: 218081521} + - {fileID: 127719938} + - {fileID: 1555484938} + - {fileID: 1876148967} + - {fileID: 1228425737} + - {fileID: 801020416} + - {fileID: 315895886} + - {fileID: 1768551574} + - {fileID: 1850807847} + - {fileID: 53988357} + - {fileID: 297623419} + - {fileID: 1946060858} + - {fileID: 1380444550} + - {fileID: 643205569} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &323745809 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 323745810} + - component: {fileID: 323745813} + - component: {fileID: 323745812} + - component: {fileID: 323745811} + m_Layer: 0 + m_Name: Cube (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &323745810 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 323745809} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 0.8224261, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &323745811 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 323745809} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &323745812 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 323745809} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &323745813 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 323745809} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &341472299 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 341472300} + - component: {fileID: 341472303} + - component: {fileID: 341472302} + - component: {fileID: 341472301} + m_Layer: 0 + m_Name: Cube (26) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &341472300 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341472299} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: -1.6600001, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &341472301 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341472299} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &341472302 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341472299} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &341472303 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 341472299} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &351532141 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 351532142} + - component: {fileID: 351532145} + - component: {fileID: 351532144} + - component: {fileID: 351532143} + m_Layer: 0 + m_Name: Cube (14) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &351532142 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 351532141} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 0.8124263, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &351532143 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 351532141} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &351532144 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 351532141} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &351532145 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 351532141} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &375087531 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 375087532} + - component: {fileID: 375087535} + - component: {fileID: 375087534} + - component: {fileID: 375087533} + m_Layer: 0 + m_Name: Cube (18) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &375087532 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 375087531} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: -1.67, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &375087533 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 375087531} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &375087534 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 375087531} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &375087535 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 375087531} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &387856194 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 387856195} + - component: {fileID: 387856198} + - component: {fileID: 387856197} + - component: {fileID: 387856196} + m_Layer: 0 + m_Name: Cube (64) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &387856195 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 387856194} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 4.92, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &387856196 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 387856194} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &387856197 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 387856194} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &387856198 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 387856194} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &443249059 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 443249060} + - component: {fileID: 443249063} + - component: {fileID: 443249062} + - component: {fileID: 443249061} + m_Layer: 0 + m_Name: Cube (42) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &443249060 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 443249059} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 4.92, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &443249061 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 443249059} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &443249062 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 443249059} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &443249063 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 443249059} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &460271676 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 460271677} + - component: {fileID: 460271679} + - component: {fileID: 460271678} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &460271677 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 511172213} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -28.681885, y: -20.492146} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &460271678 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 48 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 24 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open Script +--- !u!222 &460271679 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 460271676} + m_CullTransparentMesh: 0 +--- !u!1 &500782908 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 500782909} + - component: {fileID: 500782912} + - component: {fileID: 500782911} + - component: {fileID: 500782910} + m_Layer: 0 + m_Name: Cube (41) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &500782909 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500782908} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 4.92, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &500782910 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500782908} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &500782911 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500782908} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &500782912 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500782908} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &511172212 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 511172213} + - component: {fileID: 511172216} + - component: {fileID: 511172215} + - component: {fileID: 511172214} + m_Layer: 5 + m_Name: Button open script + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &511172213 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.6553401, y: 0.6553401, z: 0.6553401} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 460271677} + m_Father: {fileID: 649153321} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 159, y: 36} + m_SizeDelta: {x: 392.12805, y: 72.27574} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &511172214 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 511172215} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &511172215 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &511172216 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 511172212} + m_CullTransparentMesh: 0 +--- !u!1 &519530842 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519530843} + - component: {fileID: 519530846} + - component: {fileID: 519530845} + - component: {fileID: 519530844} + m_Layer: 0 + m_Name: Cube (53) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &519530843 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519530842} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 4.91, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &519530844 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519530842} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &519530845 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519530842} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &519530846 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519530842} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &523436226 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 523436227} + - component: {fileID: 523436230} + - component: {fileID: 523436229} + - component: {fileID: 523436228} + m_Layer: 0 + m_Name: Cube (45) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &523436227 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523436226} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 2.437574, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &523436228 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523436226} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &523436229 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523436226} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &523436230 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 523436226} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &582255911 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 582255912} + - component: {fileID: 582255915} + - component: {fileID: 582255914} + - component: {fileID: 582255913} + m_Layer: 0 + m_Name: Cube (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &582255912 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582255911} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 0.8224261, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &582255913 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582255911} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &582255914 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582255911} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &582255915 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 582255911} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &589500251 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 589500252} + - component: {fileID: 589500255} + - component: {fileID: 589500254} + - component: {fileID: 589500253} + m_Layer: 0 + m_Name: Cube (59) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &589500252 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589500251} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 2.427574, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &589500253 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589500251} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &589500254 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589500251} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &589500255 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 589500251} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &643205568 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 643205569} + - component: {fileID: 643205572} + - component: {fileID: 643205571} + - component: {fileID: 643205570} + m_Layer: 0 + m_Name: Cube (79) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &643205569 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 643205568} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 2.427574, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &643205570 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 643205568} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &643205571 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 643205568} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &643205572 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 643205568} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &649153317 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 649153321} + - component: {fileID: 649153320} + - component: {fileID: 649153319} + - component: {fileID: 649153318} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &649153318 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &649153319 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1000, y: 557} + m_ScreenMatchMode: 1 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &649153320 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &649153321 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 649153317} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1537524790} + - {fileID: 511172213} + - {fileID: 1847025553} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &667369430 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 667369431} + - component: {fileID: 667369434} + - component: {fileID: 667369433} + - component: {fileID: 667369432} + m_Layer: 0 + m_Name: Cube (17) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &667369431 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 667369430} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: -1.6699998, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &667369432 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 667369430} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &667369433 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 667369430} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &667369434 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 667369430} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &677037661 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 677037662} + - component: {fileID: 677037665} + - component: {fileID: 677037664} + - component: {fileID: 677037663} + m_Layer: 0 + m_Name: Cube (28) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &677037662 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 677037661} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: -1.6600001, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &677037663 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 677037661} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &677037664 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 677037661} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &677037665 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 677037661} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &700195177 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 700195180} + - component: {fileID: 700195179} + - component: {fileID: 700195178} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &700195178 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &700195179 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &700195180 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 700195177} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &738032837 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 738032838} + - component: {fileID: 738032841} + - component: {fileID: 738032840} + - component: {fileID: 738032839} + m_Layer: 0 + m_Name: Cube (49) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &738032838 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 738032837} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 2.4375737, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &738032839 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 738032837} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &738032840 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 738032837} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &738032841 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 738032837} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &743324178 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 743324179} + - component: {fileID: 743324182} + - component: {fileID: 743324181} + - component: {fileID: 743324180} + m_Layer: 0 + m_Name: Cube (32) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &743324179 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 743324178} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 0.8124263, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &743324180 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 743324178} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &743324181 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 743324178} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &743324182 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 743324178} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &768601851 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 768601852} + - component: {fileID: 768601855} + - component: {fileID: 768601854} + - component: {fileID: 768601853} + m_Layer: 0 + m_Name: Cube (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &768601852 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768601851} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: -1.6600001, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &768601853 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768601851} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &768601854 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768601851} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &768601855 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 768601851} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &778191807 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 778191808} + - component: {fileID: 778191811} + - component: {fileID: 778191810} + - component: {fileID: 778191809} + m_Layer: 0 + m_Name: Cube (61) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &778191808 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778191807} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 4.92, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &778191809 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778191807} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &778191810 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778191807} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &778191811 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 778191807} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &781926842 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 781926843} + - component: {fileID: 781926846} + - component: {fileID: 781926845} + - component: {fileID: 781926844} + m_Layer: 0 + m_Name: Cube (25) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &781926843 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 781926842} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: -1.66, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &781926844 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 781926842} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &781926845 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 781926842} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &781926846 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 781926842} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &792419465 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 792419466} + - component: {fileID: 792419469} + - component: {fileID: 792419468} + - component: {fileID: 792419467} + m_Layer: 0 + m_Name: Cube (38) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &792419466 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 792419465} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: -1.67, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &792419467 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 792419465} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &792419468 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 792419465} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &792419469 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 792419465} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &801020415 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 801020416} + - component: {fileID: 801020419} + - component: {fileID: 801020418} + - component: {fileID: 801020417} + m_Layer: 0 + m_Name: Cube (71) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &801020416 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 801020415} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 4.91, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &801020417 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 801020415} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &801020418 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 801020415} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &801020419 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 801020415} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &835270568 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 835270569} + - component: {fileID: 835270572} + - component: {fileID: 835270571} + - component: {fileID: 835270570} + m_Layer: 0 + m_Name: Cube (51) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &835270569 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 835270568} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 4.91, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &835270570 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 835270568} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &835270571 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 835270568} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &835270572 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 835270568} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &893274497 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 893274498} + - component: {fileID: 893274501} + - component: {fileID: 893274500} + - component: {fileID: 893274499} + m_Layer: 0 + m_Name: Cube (40) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &893274498 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893274497} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 4.92, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &893274499 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893274497} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &893274500 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893274497} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &893274501 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 893274497} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &927111011 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 927111012} + - component: {fileID: 927111015} + - component: {fileID: 927111014} + - component: {fileID: 927111013} + m_Layer: 0 + m_Name: Cube (20) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &927111012 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 927111011} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 0.8224261, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &927111013 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 927111011} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &927111014 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 927111011} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &927111015 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 927111011} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &928584461 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 928584462} + - component: {fileID: 928584465} + - component: {fileID: 928584464} + - component: {fileID: 928584463} + m_Layer: 0 + m_Name: Cube (35) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &928584462 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 928584461} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: -1.6699998, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &928584463 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 928584461} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &928584464 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 928584461} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &928584465 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 928584461} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &965437870 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 965437872} + - component: {fileID: 965437871} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &965437871 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965437870} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &965437872 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 965437870} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1010368908 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1010368909} + - component: {fileID: 1010368912} + - component: {fileID: 1010368911} + - component: {fileID: 1010368910} + m_Layer: 0 + m_Name: Cube (23) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1010368909 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1010368908} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 0.8224261, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1010368910 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1010368908} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1010368911 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1010368908} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1010368912 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1010368908} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1019461521 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1019461522} + - component: {fileID: 1019461525} + - component: {fileID: 1019461524} + - component: {fileID: 1019461523} + m_Layer: 0 + m_Name: Cube (55) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1019461522 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1019461521} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 2.427574, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1019461523 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1019461521} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1019461524 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1019461521} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1019461525 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1019461521} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1069134372 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1069134373} + - component: {fileID: 1069134376} + - component: {fileID: 1069134375} + - component: {fileID: 1069134374} + m_Layer: 0 + m_Name: Cube (36) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1069134373 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1069134372} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: -1.6699998, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1069134374 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1069134372} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1069134375 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1069134372} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1069134376 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1069134372} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1101930858 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1101930859} + - component: {fileID: 1101930861} + - component: {fileID: 1101930860} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1101930859 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1847025553} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -28.681885, y: -20.492146} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1101930860 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 48 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 24 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open Editor tab +--- !u!222 &1101930861 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1101930858} + m_CullTransparentMesh: 0 +--- !u!1 &1123278459 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1123278460} + - component: {fileID: 1123278463} + - component: {fileID: 1123278462} + - component: {fileID: 1123278461} + m_Layer: 0 + m_Name: Cube (34) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1123278460 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123278459} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 0.8124263, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1123278461 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123278459} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1123278462 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123278459} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1123278463 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123278459} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1124320614 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1124320615} + - component: {fileID: 1124320618} + - component: {fileID: 1124320617} + - component: {fileID: 1124320616} + m_Layer: 0 + m_Name: Cube (19) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1124320615 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1124320614} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: -1.6699998, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1124320616 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1124320614} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1124320617 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1124320614} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1124320618 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1124320614} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1130193476 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1130193477} + - component: {fileID: 1130193480} + - component: {fileID: 1130193479} + - component: {fileID: 1130193478} + m_Layer: 0 + m_Name: Cube (44) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1130193477 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1130193476} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 4.92, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1130193478 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1130193476} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1130193479 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1130193476} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1130193480 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1130193476} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1218495766 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1218495767} + - component: {fileID: 1218495770} + - component: {fileID: 1218495769} + - component: {fileID: 1218495768} + m_Layer: 0 + m_Name: Cube (60) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1218495767 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218495766} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 4.92, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1218495768 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218495766} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1218495769 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218495766} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1218495770 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1218495766} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1228425736 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1228425737} + - component: {fileID: 1228425740} + - component: {fileID: 1228425739} + - component: {fileID: 1228425738} + m_Layer: 0 + m_Name: Cube (70) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1228425737 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228425736} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 4.91, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1228425738 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228425736} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1228425739 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228425736} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1228425740 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228425736} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1231537574 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1231537575} + - component: {fileID: 1231537578} + - component: {fileID: 1231537577} + - component: {fileID: 1231537576} + m_Layer: 0 + m_Name: Cube (57) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1231537575 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1231537574} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 2.427574, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1231537576 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1231537574} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1231537577 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1231537574} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1231537578 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1231537574} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1281233702 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1281233703} + - component: {fileID: 1281233706} + - component: {fileID: 1281233705} + - component: {fileID: 1281233704} + m_Layer: 0 + m_Name: Cube (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1281233703 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1281233702} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: -1.6600001, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1281233704 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1281233702} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1281233705 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1281233702} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1281233706 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1281233702} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1343033882 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1343033883} + - component: {fileID: 1343033886} + - component: {fileID: 1343033885} + - component: {fileID: 1343033884} + m_Layer: 0 + m_Name: Cube (24) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1343033883 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1343033882} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 0.8224261, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1343033884 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1343033882} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1343033885 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1343033882} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1343033886 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1343033882} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1380444549 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1380444550} + - component: {fileID: 1380444553} + - component: {fileID: 1380444552} + - component: {fileID: 1380444551} + m_Layer: 0 + m_Name: Cube (78) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1380444550 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1380444549} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 2.4275737, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1380444551 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1380444549} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1380444552 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1380444549} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1380444553 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1380444549} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1399883245 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1399883246} + - component: {fileID: 1399883249} + - component: {fileID: 1399883248} + - component: {fileID: 1399883247} + m_Layer: 0 + m_Name: Cube (31) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1399883246 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1399883245} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 0.8124263, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1399883247 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1399883245} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1399883248 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1399883245} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1399883249 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1399883245} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1500025042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1500025043} + - component: {fileID: 1500025046} + - component: {fileID: 1500025045} + - component: {fileID: 1500025044} + m_Layer: 0 + m_Name: Cube (15) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1500025043 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1500025042} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: -1.6699998, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1500025044 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1500025042} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1500025045 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1500025042} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1500025046 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1500025042} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1537524789 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1537524790} + - component: {fileID: 1537524792} + - component: {fileID: 1537524791} + m_Layer: 5 + m_Name: InformationText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1537524790 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 649153321} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -0.00000095367, y: 253} + m_SizeDelta: {x: 861.9848, y: 122.55513} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1537524791 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 56 + m_FontStyle: 1 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 64 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Hot Reload is not running yet +--- !u!222 &1537524792 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1537524789} + m_CullTransparentMesh: 0 +--- !u!1 &1555484937 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1555484938} + - component: {fileID: 1555484941} + - component: {fileID: 1555484940} + - component: {fileID: 1555484939} + m_Layer: 0 + m_Name: Cube (68) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1555484938 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1555484937} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 2.4375737, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1555484939 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1555484937} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1555484940 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1555484937} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1555484941 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1555484937} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1586797430 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1586797431} + - component: {fileID: 1586797434} + - component: {fileID: 1586797433} + - component: {fileID: 1586797432} + m_Layer: 0 + m_Name: Cube (33) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1586797431 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586797430} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 0.8124263, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1586797432 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586797430} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1586797433 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586797430} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1586797434 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1586797430} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1600668862 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1600668863} + - component: {fileID: 1600668866} + - component: {fileID: 1600668865} + - component: {fileID: 1600668864} + m_Layer: 0 + m_Name: Cube (46) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1600668863 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600668862} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 2.4375737, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1600668864 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600668862} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1600668865 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600668862} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1600668866 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1600668862} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1682534255 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1682534256} + - component: {fileID: 1682534259} + - component: {fileID: 1682534258} + - component: {fileID: 1682534257} + m_Layer: 0 + m_Name: Cube (12) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1682534256 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1682534255} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 0.8124263, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1682534257 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1682534255} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1682534258 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1682534255} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1682534259 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1682534255} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1768551573 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1768551574} + - component: {fileID: 1768551577} + - component: {fileID: 1768551576} + - component: {fileID: 1768551575} + m_Layer: 0 + m_Name: Cube (73) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1768551574 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1768551573} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 4.91, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1768551575 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1768551573} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1768551576 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1768551573} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1768551577 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1768551573} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1774902064 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1774902065} + - component: {fileID: 1774902068} + - component: {fileID: 1774902067} + - component: {fileID: 1774902066} + m_Layer: 0 + m_Name: Cube (54) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1774902065 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1774902064} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 4.91, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1774902066 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1774902064} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1774902067 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1774902064} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1774902068 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1774902064} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1847025552 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1847025553} + - component: {fileID: 1847025556} + - component: {fileID: 1847025555} + - component: {fileID: 1847025554} + m_Layer: 5 + m_Name: Button open editor tab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1847025553 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.65534, y: 0.65534, z: 0.65534} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1101930859} + m_Father: {fileID: 649153321} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -153, y: 36} + m_SizeDelta: {x: 393.12805, y: 73.27576} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1847025554 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1847025555} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1847025555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1847025556 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847025552} + m_CullTransparentMesh: 0 +--- !u!1 &1850807846 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1850807847} + - component: {fileID: 1850807850} + - component: {fileID: 1850807849} + - component: {fileID: 1850807848} + m_Layer: 0 + m_Name: Cube (74) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1850807847 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850807846} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 4.91, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1850807848 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850807846} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1850807849 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850807846} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1850807850 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1850807846} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1855770256 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1855770257} + - component: {fileID: 1855770260} + - component: {fileID: 1855770259} + - component: {fileID: 1855770258} + m_Layer: 0 + m_Name: Cube (39) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1855770257 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1855770256} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: -1.6699998, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1855770258 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1855770256} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1855770259 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1855770256} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1855770260 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1855770256} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1876148966 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1876148967} + - component: {fileID: 1876148970} + - component: {fileID: 1876148969} + - component: {fileID: 1876148968} + m_Layer: 0 + m_Name: Cube (69) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1876148967 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1876148966} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: 2.4375737, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1876148968 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1876148966} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1876148969 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1876148966} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1876148970 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1876148966} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1893598705 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1893598706} + - component: {fileID: 1893598709} + - component: {fileID: 1893598708} + - component: {fileID: 1893598707} + m_Layer: 0 + m_Name: Cube (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1893598706 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1893598705} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 0.8224261, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1893598707 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1893598705} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1893598708 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1893598705} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1893598709 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1893598705} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1927368434 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1927368435} + - component: {fileID: 1927368438} + - component: {fileID: 1927368437} + - component: {fileID: 1927368436} + m_Layer: 0 + m_Name: Cube (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1927368435 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1927368434} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 0.8124263, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1927368436 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1927368434} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1927368437 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1927368434} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1927368438 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1927368434} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1931512539 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1931512540} + - component: {fileID: 1931512543} + - component: {fileID: 1931512542} + - component: {fileID: 1931512541} + m_Layer: 0 + m_Name: Cube (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1931512540 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1931512539} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: -1.6600001, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1931512541 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1931512539} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1931512542 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1931512539} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1931512543 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1931512539} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1946060857 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1946060858} + - component: {fileID: 1946060861} + - component: {fileID: 1946060860} + - component: {fileID: 1946060859} + m_Layer: 0 + m_Name: Cube (77) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1946060858 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946060857} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 2.427574, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1946060859 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946060857} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1946060860 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946060857} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1946060861 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1946060857} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1965297414 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1965297415} + - component: {fileID: 1965297418} + - component: {fileID: 1965297417} + - component: {fileID: 1965297416} + m_Layer: 0 + m_Name: Cube (58) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1965297415 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965297414} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 2.4275737, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1965297416 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965297414} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1965297417 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965297414} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1965297418 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1965297414} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1973025878 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1973025879} + - component: {fileID: 1973025882} + - component: {fileID: 1973025881} + - component: {fileID: 1973025880} + m_Layer: 0 + m_Name: Cube (29) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1973025879 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1973025878} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 5.4900002, y: -1.6600001, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1973025880 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1973025878} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1973025881 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1973025878} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1973025882 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1973025878} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1983101810 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1983101811} + - component: {fileID: 1983101814} + - component: {fileID: 1983101813} + - component: {fileID: 1983101812} + m_Layer: 0 + m_Name: Cube (63) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1983101811 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1983101810} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 1.73, y: 4.92, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1983101812 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1983101810} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1983101813 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1983101810} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1983101814 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1983101810} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2014093029 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2014093030} + - component: {fileID: 2014093033} + - component: {fileID: 2014093032} + - component: {fileID: 2014093031} + m_Layer: 0 + m_Name: Cube (37) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2014093030 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2014093029} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: -1.6699998, z: 5.4483223} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2014093031 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2014093029} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2014093032 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2014093029} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2014093033 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2014093029} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2042268980 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2042268981} + - component: {fileID: 2042268984} + - component: {fileID: 2042268983} + - component: {fileID: 2042268982} + m_Layer: 0 + m_Name: Cube (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2042268981 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042268980} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -7, y: 0.8124263, z: 1.85} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2042268982 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042268980} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2042268983 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042268980} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2042268984 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042268980} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2053621921 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2053621922} + - component: {fileID: 2053621925} + - component: {fileID: 2053621924} + - component: {fileID: 2053621923} + m_Layer: 0 + m_Name: Cube (47) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2053621922 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2053621921} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 2.4375737, z: 0.0616778} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2053621923 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2053621921} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2053621924 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2053621921} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2053621925 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2053621921} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2063974668 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2063974669} + - component: {fileID: 2063974672} + - component: {fileID: 2063974671} + - component: {fileID: 2063974670} + m_Layer: 0 + m_Name: Cube (22) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2063974669 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2063974668} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: 0.8224261, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2063974670 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2063974668} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2063974671 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2063974668} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2063974672 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2063974668} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2102038395 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2102038396} + - component: {fileID: 2102038399} + - component: {fileID: 2102038398} + - component: {fileID: 2102038397} + m_Layer: 0 + m_Name: Cube (65) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2102038396 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2102038395} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -4.19, y: 2.437574, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2102038397 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2102038395} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2102038398 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2102038395} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2102038399 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2102038395} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2132145875 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2132145876} + - component: {fileID: 2132145877} + m_Layer: 0 + m_Name: HotReloadDemo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2132145876 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2132145875} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 18.716805, y: 53.419094, z: 172.31} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2132145877 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2132145875} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e09948cf1f317d04fbaf410dbfe91656, type: 3} + m_Name: + m_EditorClassIdentifier: + cubes: + - {fileID: 19295893} + - {fileID: 323745810} + - {fileID: 582255912} + - {fileID: 1893598706} + - {fileID: 132063620} + - {fileID: 297017160} + - {fileID: 768601852} + - {fileID: 1931512540} + - {fileID: 280025524} + - {fileID: 1281233703} + - {fileID: 1927368435} + - {fileID: 2042268981} + - {fileID: 1682534256} + - {fileID: 181686443} + - {fileID: 351532142} + - {fileID: 1500025043} + - {fileID: 69029315} + - {fileID: 667369431} + - {fileID: 375087532} + - {fileID: 1124320615} + - {fileID: 927111012} + - {fileID: 266848584} + - {fileID: 2063974669} + - {fileID: 1010368909} + - {fileID: 1343033883} + - {fileID: 781926843} + - {fileID: 341472300} + - {fileID: 2136109399} + - {fileID: 677037662} + - {fileID: 1973025879} + - {fileID: 262969855} + - {fileID: 1399883246} + - {fileID: 743324179} + - {fileID: 1586797431} + - {fileID: 1123278460} + - {fileID: 928584462} + - {fileID: 1069134373} + - {fileID: 2014093030} + - {fileID: 792419466} + - {fileID: 1855770257} + - {fileID: 893274498} + - {fileID: 500782909} + - {fileID: 443249060} + - {fileID: 121342031} + - {fileID: 1130193477} + - {fileID: 523436227} + - {fileID: 1600668863} + - {fileID: 2053621922} + - {fileID: 40618804} + - {fileID: 738032838} + - {fileID: 249919995} + - {fileID: 835270569} + - {fileID: 282541333} + - {fileID: 519530843} + - {fileID: 1774902065} + - {fileID: 1019461522} + - {fileID: 128004586} + - {fileID: 1231537575} + - {fileID: 1965297415} + - {fileID: 589500252} + - {fileID: 1218495767} + - {fileID: 778191808} + - {fileID: 133838189} + - {fileID: 1983101811} + - {fileID: 387856195} + - {fileID: 2102038396} + - {fileID: 218081521} + - {fileID: 127719938} + - {fileID: 1555484938} + - {fileID: 1876148967} + - {fileID: 1228425737} + - {fileID: 801020416} + - {fileID: 315895886} + - {fileID: 1768551574} + - {fileID: 1850807847} + - {fileID: 53988357} + - {fileID: 297623419} + - {fileID: 1946060858} + - {fileID: 1380444550} + - {fileID: 643205569} + informationText: {fileID: 1537524791} + openWindowButton: {fileID: 1847025554} + openScriptButton: {fileID: 511172214} + thisScript: {fileID: 11500000, guid: e09948cf1f317d04fbaf410dbfe91656, type: 3} +--- !u!1 &2136109398 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2136109399} + - component: {fileID: 2136109402} + - component: {fileID: 2136109401} + - component: {fileID: 2136109400} + m_Layer: 0 + m_Name: Cube (27) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2136109399 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2136109398} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.36, y: -1.6600001, z: 3.66} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 321495840} + m_RootOrder: -1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2136109400 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2136109398} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2136109401 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2136109398} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 0 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2136109402 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2136109398} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity.meta new file mode 100644 index 0000000..20570d2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: fad9aa54ab3335844b5a35b9eb6ae286 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting new file mode 100644 index 0000000..3c301a1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting @@ -0,0 +1,66 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!850595691 &4890085278179872738 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: HotReloadBurstDemoSettings + serializedVersion: 6 + m_GIWorkflowMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_LightmapCompression: 3 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 512 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentImportanceSampling: 0 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 + m_NumRaysToShootPerTexel: -1 + m_RespectSceneVisibilityWhenBakingGI: 0 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting.meta new file mode 100644 index 0000000..68e5ea1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 961e97ae3d4011b47a1198a930f5c30d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 4890085278179872738 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts.meta new file mode 100644 index 0000000..f542db8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 30c72b28fb747184ba79468d3571dea4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs new file mode 100644 index 0000000..316c503 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs @@ -0,0 +1,217 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload.Demo { + class HotReloadBasicDemo : MonoBehaviour { + + public GameObject cube; + public Text informationText; + public Button openWindowButton; + public Button openScriptButton; + public TextAsset thisScript; + + // // 1. Adding fields (Added fields can show in the inspector) + // public int myNewField = 1; + + void Start() { + if (Application.isEditor) { + openWindowButton.onClick.AddListener(Demo.I.OpenHotReloadWindow); + openScriptButton.onClick.AddListener(() => Demo.I.OpenScriptFile(thisScript, myStaticField, 13)); + } else { + openWindowButton.gameObject.SetActive(false); + openScriptButton.gameObject.SetActive(false); + informationText.gameObject.SetActive(false); + } + } + + // Update is called once per frame + void Update() { + if (Demo.I.IsServerRunning()) { + informationText.text = Localization.Translations.Common.HotReloadIsRunning; + } else { + informationText.text = Localization.Translations.Common.HotReloadIsNotRunning; + } + + // // 2. Editing functions in monobehaviours, normal classes or static classes + // // Edit the vector to rotate the cube in the scene differently or change the speed + // var speed = 100; + // cube.transform.Rotate(new Vector3(0, 1, 0) * Time.deltaTime * speed); + + // // 2. Editing functions in monobehaviours, normal classes or static classes + // // Uncomment this code to scale the cube + // cube.transform.localScale = Mathf.Sin(Time.time) * Vector3.one; + + // // 2. Editing functions in monobehaviours, normal classes or static classes + // // Uncomment this code to make the cube move from left to right and back + // var newPos = cube.transform.position += (cube.transform.localScale.x < 0.5 ? Vector3.left : Vector3.right) * Time.deltaTime; + // if(Mathf.Abs(newPos.x) > 10) { + // cube.transform.position = Vector3.zero; + // } + } + + // 3. Editing lambda methods + static Func addFunction = x => { + var result = x + 10; + Debug.Log("Add: " + result); + // // uncomment to change the operator to multiply and log the result + // result = x * 10; + // Debug.Log("Multiply: " + result); + return result; + }; + + // 4. Editing async/await methods + async Task AsyncMethod() { + // await Task.Delay(500); + // Debug.Log("AsyncMethod"); + + // // silicense warning + await Task.CompletedTask; + } + + // 5. Editing properties (get/set) + public static string SomeString { + // edit the get method + get { + var someStringHere = "This is some string"; + return someStringHere; + } + } + + // 6. Editing indexers (square bracket access such as dictionaries) + class CustomDictionary : Dictionary { + public new int this[string key] { + get { + // // uncomment to change the indexer and log a different entry based on case + // return base[key.ToLower()]; + return base[key.ToUpper()]; + } + set { + base[key.ToUpper()] = value; + } + } + } + CustomDictionary randomDict = new CustomDictionary { + { "a", 4 }, + { "A", 5 }, + { "b", 9 }, + { "B", 10 }, + { "c", 14 }, + { "C", 15 }, + { "d", 19 }, + { "D", 20 } + }; + + // 7. Editing operators methods (explicit and implicit operators) + public class Email { + public string Value { get; } + + public Email(string value) { + Value = value; + } + + // Define implicit operator + public static implicit operator string(Email value) + // Uncomment to change the implicit operator + // => value.Value + " FOO"; + => value.Value; + + // // Uncomment to change add an implicit operator + // public static implicit operator byte[](Email value) + // => Encoding.UTF8.GetBytes(value.Value); + + // Define explicit operator + public static explicit operator Email(string value) + => new Email(value); + } + + // 8. Editing fields: modifiers/type/name/initializer + public int myEditedField = 4; + + // 9. Editing static field initializers (variable value is updated) + static readonly int myStaticField = 31; + + // // 10. Adding auto properties/events + // int MyProperty { get; set; } = 6; + // event Action MyEvent = () => Debug.Log("MyEvent"); + + class GenericClass { + // // 11. Adding methods in generic classes + // public void GenericMethod() { + // Debug.Log("GenericMethod"); + // } + // // 12. Adding fields (any type) in generic classes + // public T myGenericField; + } + + void LateUpdate() { + // // 3. Editing lambda methods + // addFunction(10); + + + // // 4. Editing async/await methods + // AsyncMethod().Forget(); + + + // // 5. Editing properties (get/set) + // Debug.Log(SomeString); + + + // // 6. Editing indexers (square bracket access such as dictionaries) + // Debug.Log(randomDict["A"]); + + + // // 7. Editing operators methods (explicit and implicit operators) + Email email = new Email("example@example.com"); + // string stringEmail = email; + // Debug.Log(stringEmail); + + // // Uncomment new operator in Email class + Uncomment this to add byte implicit operator + // byte[] byteEmail = email; + // var hexRepresentation = BitConverter.ToString(byteEmail); + // Debug.Log(hexRepresentation); + // Debug.Log(Encoding.UTF8.GetString(byteEmail)); + + // // 8. Editing fields: modifiers/type/name/initializer + // Debug.Log("myEditedField: " + myEditedField); + + // // 9. Editing static field initializers (variable value is updated) + // Debug.Log("myStaticField: " + myStaticField); + + // // 10. Adding auto properties/events + // Debug.Log("MyProperty: " + MyProperty); + // MyEvent.Invoke(); + + // var newClass = new GenericClass(); + // // 11. Adding methods in generic classes + // newClass.GenericMethod(); + // // 12. Adding fields in generic classes + // newClass.myGenericField = 3; + // Debug.Log("myGenericField: " + newClass.myGenericField); + + // // 13. Editing lambda methods with closures + // // Uncomment to log sorted array + // // Switch a and b to reverse the sorting + // int[] numbers = { 5, 3, 8, 1, 9 }; + // Array.Sort(numbers, (b, a) => a.CompareTo(b)); + // Debug.Log(string.Join(", ", numbers)); + } + + // This function gets invoked every time it's patched + [InvokeOnHotReloadLocal] + static void OnHotReloadMe() { + // // change the string to see the method getting invoked + // Debug.Log("Hello there"); + } + + // // 14. Adding event functions + // void OnDisable() { + // Debug.Log("OnDisable"); + // } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs.meta new file mode 100644 index 0000000..7b8918d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5a2e4d3f095a9441688c70278068eee0 +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/Runtime/Demo/Scripts/HotReloadBasicDemo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs new file mode 100644 index 0000000..03f29c8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs @@ -0,0 +1,63 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Jobs; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload.Demo { + public class HotReloadBurstJobsDemo : MonoBehaviour { + public Transform[] cubes; + public Text informationText; + public Button openWindowButton; + public Button openScriptButton; + public TextAsset thisScript; + + TransformAccessArray cubeTransforms; + CubeJob job; + void Awake() { + cubeTransforms = new TransformAccessArray(cubes); + if(Application.isEditor) { + openWindowButton.onClick.AddListener(Demo.I.OpenHotReloadWindow); + openScriptButton.onClick.AddListener(() => Demo.I.OpenScriptFile(thisScript, 49, 17)); + } else { + openWindowButton.gameObject.SetActive(false); + openScriptButton.gameObject.SetActive(false); + } + informationText.gameObject.SetActive(true); + } + + void Update() { + job.deltaTime = Time.deltaTime; + job.time = Time.time; + var handle = job.Schedule(cubeTransforms); + handle.Complete(); + + if (Demo.I.IsServerRunning()) { + informationText.text = Localization.Translations.Common.HotReloadIsRunning; + } else { + informationText.text = Localization.Translations.Common.HotReloadIsNotRunning; + } + } + + struct CubeJob : IJobParallelForTransform { + public float deltaTime; + public float time; + public void Execute(int index, TransformAccess transform) { + transform.localRotation *= Quaternion.Euler(50 * deltaTime, 0, 0); + + // Uncomment this code to scale the cubes + // var scale = Mathf.Abs(Mathf.Sin(time)); + // transform.localScale = new Vector3(scale, scale, scale); + + // Uncomment this code to make the cube move from left to right and back + // transform.position += (transform.localScale.x < 0.5 ? Vector3.left : Vector3.right) * deltaTime; + } + } + + void OnDestroy() { + cubeTransforms.Dispose(); + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs.meta new file mode 100644 index 0000000..fcf90c2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e09948cf1f317d04fbaf410dbfe91656 +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/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs new file mode 100644 index 0000000..ca2034a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs @@ -0,0 +1,29 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using UnityEngine; + +namespace SingularityGroup.HotReload.Demo { + public interface IDemo { + bool IsServerRunning(); + void OpenHotReloadWindow(); + void OpenScriptFile(TextAsset textAsset, int line, int column); + } + + public static class Demo { + public static IDemo I = new PlayerDemo(); + } + + public class PlayerDemo : IDemo { + public bool IsServerRunning() { + return ServerHealthCheck.I.IsServerHealthy; + } + + public void OpenHotReloadWindow() { + //no-op + } + + public void OpenScriptFile(TextAsset textAsset, int line, int column) { + //no-op + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs.meta new file mode 100644 index 0000000..bc74171 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 04dccdcced0245f1830021fdcad1d28a +timeCreated: 1677321944 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab new file mode 100644 index 0000000..85d6761 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab @@ -0,0 +1,2942 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1013787301382345451 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3597930498506735329} + - component: {fileID: 5263297665501092759} + - component: {fileID: 8191138318542799492} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3597930498506735329 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1013787301382345451} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 8361365728969909008} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5263297665501092759 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1013787301382345451} + m_CullTransparentMesh: 0 +--- !u!114 &8191138318542799492 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1013787301382345451} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.11320752, g: 0.11320752, b: 0.11320752, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 22 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Cancel +--- !u!1 &1057795414473985365 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8361365728969909008} + - component: {fileID: 9109116132926969505} + - component: {fileID: 6961214002816918688} + - component: {fileID: 5585168207715079851} + m_Layer: 5 + m_Name: ButtonMoreEffort + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8361365728969909008 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1057795414473985365} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3597930498506735329} + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -423.1, y: -64.9} + m_SizeDelta: {x: 141.6914, y: 45.0679} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &9109116132926969505 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1057795414473985365} + m_CullTransparentMesh: 0 +--- !u!114 &6961214002816918688 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1057795414473985365} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &5585168207715079851 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1057795414473985365} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 0.9622642, g: 0.9622642, b: 0.9622642, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 6961214002816918688} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &1335534115928082901 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5505278863775282652} + - component: {fileID: 882497356905571160} + - component: {fileID: 6369210938302316831} + m_Layer: 5 + m_Name: TextSummary + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5505278863775282652 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1335534115928082901} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 30, y: -39.233} + m_SizeDelta: {x: -105.96521, y: 54.542114} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &882497356905571160 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1335534115928082901} + m_CullTransparentMesh: 0 +--- !u!114 &6369210938302316831 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1335534115928082901} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Editor and current build are on different commits +--- !u!1 &1390084864838268853 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1221019002237951643} + - component: {fileID: 8358362994993817161} + - component: {fileID: 1980611848569999305} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1221019002237951643 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1390084864838268853} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 7107734678944665722} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -35.811356, y: -12.790634} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &8358362994993817161 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1390084864838268853} + m_CullTransparentMesh: 0 +--- !u!114 &1980611848569999305 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1390084864838268853} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.103773594, g: 0.103773594, b: 0.103773594, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 21 + m_MaxSize: 28 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Continue +--- !u!1 &2338911661825597671 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4216036513099635638} + - component: {fileID: 594918778888372109} + - component: {fileID: 2127224386387722146} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4216036513099635638 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2338911661825597671} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4911193491485015256} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &594918778888372109 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2338911661825597671} + m_CullTransparentMesh: 0 +--- !u!114 &2127224386387722146 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2338911661825597671} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.103773594, g: 0.103773594, b: 0.103773594, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: More Info +--- !u!1 &2557231470263189725 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6280529082113425347} + m_Layer: 5 + m_Name: Information + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6280529082113425347 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2557231470263189725} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3344052376368028088} + - {fileID: 7593666350427564864} + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 30, y: -13.59} + m_SizeDelta: {x: -106.39874, y: -142.43198} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2582527480827036942 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6528462525361087078} + - component: {fileID: 5585154964765544786} + - component: {fileID: 5675038352245823804} + m_Layer: 5 + m_Name: TextSuggestion + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6528462525361087078 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2582527480827036942} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 30.217, y: 6.0687} + m_SizeDelta: {x: -106.3987, y: -145.1455} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5585154964765544786 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2582527480827036942} + m_CullTransparentMesh: 0 +--- !u!114 &5675038352245823804 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2582527480827036942} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 22 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 21 + m_MaxSize: 28 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1.17 + m_Text: 'This can cause errors when the build was made on an old commit. + +' +--- !u!1 &2945586050721362106 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7593666350427564864} + - component: {fileID: 4488835628498483499} + - component: {fileID: 6495855994796430067} + m_Layer: 5 + m_Name: TextForDebugging + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &7593666350427564864 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2945586050721362106} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6280529082113425347} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4488835628498483499 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2945586050721362106} + m_CullTransparentMesh: 0 +--- !u!114 &6495855994796430067 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2945586050721362106} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 29 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Make sure you're on the same LAN/WiFi network +--- !u!1 &3342967049223911331 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3344052376368028088} + - component: {fileID: 4147603110869920048} + - component: {fileID: 116564040413298098} + m_Layer: 5 + m_Name: TextSuggestion + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3344052376368028088 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3342967049223911331} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6280529082113425347} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 5.6499987} + m_SizeDelta: {x: 0, y: 11.300002} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4147603110869920048 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3342967049223911331} + m_CullTransparentMesh: 0 +--- !u!114 &116564040413298098 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3342967049223911331} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 23 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 275 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Make sure you're on the same WiFi network and Hot Reload is running +--- !u!1 &3751191164850618597 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3751191164850618611} + - component: {fileID: 3751191164850618616} + - component: {fileID: 3751191164850618560} + m_Layer: 5 + m_Name: Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3751191164850618611 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618597} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086677765351015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 46.1, y: -43.161} + m_SizeDelta: {x: 54.687653, y: 54.687653} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3751191164850618616 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618597} + m_CullTransparentMesh: 0 +--- !u!114 &3751191164850618560 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618597} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 90cf8e542151548c6aa3cba26467e144, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &3751191164850618600 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3751191164850618614} + - component: {fileID: 3751191164850618595} + - component: {fileID: 3751191164850618571} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3751191164850618614 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618600} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086678661718217} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3751191164850618595 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618600} + m_CullTransparentMesh: 0 +--- !u!114 &3751191164850618571 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618600} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 22 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Hide +--- !u!1 &3751191164850618601 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3751191164850618615} + - component: {fileID: 3751191164850618620} + - component: {fileID: 3751191164850618564} + m_Layer: 5 + m_Name: Prompts + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3751191164850618615 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618601} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4967086677765351015} + - {fileID: 4967086678334773008} + - {fileID: 6484505723585156786} + m_Father: {fileID: 4967086677379066171} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -0.00024414062, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3751191164850618620 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618601} + m_CullTransparentMesh: 0 +--- !u!114 &3751191164850618564 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618601} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d92cdbfacafd433ca77184c22a384a6d, type: 3} + m_Name: + m_EditorClassIdentifier: + retryPrompt: {fileID: 4967086678334773011} + connectedPrompt: {fileID: 4967086677765351014} + questionPrompt: {fileID: 6563246299181214611} + fallbackEventSystem: {fileID: 8054601594198067103} +--- !u!1 &3751191164850618602 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3751191164850618608} + - component: {fileID: 3751191164850618621} + - component: {fileID: 3751191164850618565} + m_Layer: 5 + m_Name: Summary + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3751191164850618608 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618602} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086677765351015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 42.160004, y: -43.00023} + m_SizeDelta: {x: -109.740295, y: 54.366207} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3751191164850618621 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618602} + m_CullTransparentMesh: 0 +--- !u!114 &3751191164850618565 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3751191164850618602} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 32 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Connecting... +--- !u!1 &4116732687138738479 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8252921096633957241} + - component: {fileID: 4733576179552229060} + - component: {fileID: 2012827545077904779} + - component: {fileID: 3158748587153539730} + m_Layer: 5 + m_Name: ButtonMoreInfo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8252921096633957241 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4116732687138738479} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5758986847798381115} + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -99.72, y: -64.9} + m_SizeDelta: {x: 141.6914, y: 45.0679} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4733576179552229060 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4116732687138738479} + m_CullTransparentMesh: 0 +--- !u!114 &2012827545077904779 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4116732687138738479} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &3158748587153539730 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4116732687138738479} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 0.9622642, g: 0.9622642, b: 0.9622642, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2012827545077904779} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &4279783835045373039 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8141605075363586685} + - component: {fileID: 1129507980982577600} + - component: {fileID: 3409363427364332004} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8141605075363586685 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4279783835045373039} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 8150310283045374484} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1129507980982577600 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4279783835045373039} + m_CullTransparentMesh: 1 +--- !u!114 &3409363427364332004 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4279783835045373039} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 15 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 0 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!1 &4471505415598507920 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6235104375140882572} + - component: {fileID: 4057252686040207820} + - component: {fileID: 3554294960250654513} + m_Layer: 5 + m_Name: TextIP + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6235104375140882572 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4471505415598507920} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -248.5, y: -66.756} + m_SizeDelta: {x: 65, y: 61.4766} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4057252686040207820 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4471505415598507920} + m_CullTransparentMesh: 0 +--- !u!114 &3554294960250654513 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4471505415598507920} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'IP:' +--- !u!1 &4803576491919660416 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8857299115396528434} + - component: {fileID: 2155891017074897053} + - component: {fileID: 462332990179851889} + m_Layer: 5 + m_Name: Suggestion + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8857299115396528434 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4803576491919660416} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086677765351015} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 42.185, y: -30.900002} + m_SizeDelta: {x: -109.78961, y: -97.906265} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2155891017074897053 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4803576491919660416} + m_CullTransparentMesh: 0 +--- !u!114 &462332990179851889 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4803576491919660416} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Edit code to see changes on device... +--- !u!1 &4967086676766916185 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086676766916190} + - component: {fileID: 4967086676766916188} + - component: {fileID: 4967086676766916191} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086676766916190 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676766916185} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086677112779038} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086676766916188 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676766916185} + m_CullTransparentMesh: 0 +--- !u!114 &4967086676766916191 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676766916185} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.103773594, g: 0.103773594, b: 0.103773594, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Reconnect + +' +--- !u!1 &4967086676871555599 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086676871555596} + - component: {fileID: 4967086676871555698} + - component: {fileID: 4967086676871555597} + m_Layer: 5 + m_Name: ImageLogo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086676871555596 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676871555599} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 42.06, y: -39.16} + m_SizeDelta: {x: 54.687653, y: 54.687653} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086676871555698 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676871555599} + m_CullTransparentMesh: 0 +--- !u!114 &4967086676871555597 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086676871555599} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 90cf8e542151548c6aa3cba26467e144, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4967086677112779033 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086677112779038} + - component: {fileID: 4967086677112779037} + - component: {fileID: 4967086677112779036} + - component: {fileID: 4967086677112779039} + m_Layer: 5 + m_Name: ButtonRetry + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086677112779038 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677112779033} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4967086676766916190} + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 45.1, y: 34.638} + m_SizeDelta: {x: -465.6686, y: 45.06787} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086677112779037 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677112779033} + m_CullTransparentMesh: 0 +--- !u!114 &4967086677112779036 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677112779033} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4967086677112779039 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677112779033} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4967086677112779036} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &4967086677379066170 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086677379066171} + - component: {fileID: 3499838185132214990} + - component: {fileID: 3499838185132214991} + - component: {fileID: 3499838185132214988} + m_Layer: 5 + m_Name: HotReloadPrompts + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086677379066171 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677379066170} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 3751191164850618615} + - {fileID: 8564535462043123833} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &3499838185132214990 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677379066170} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &3499838185132214991 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677379066170} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1280, y: 720} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!114 &3499838185132214988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677379066170} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!1 &4967086677533727706 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086677533727707} + - component: {fileID: 4967086677533727705} + - component: {fileID: 4967086677533727704} + m_Layer: 5 + m_Name: TextSummary + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086677533727707 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677533727706} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 30, y: -42.7} + m_SizeDelta: {x: -105.96521, y: 61.476562} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086677533727705 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677533727706} + m_CullTransparentMesh: 0 +--- !u!114 &4967086677533727704 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677533727706} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Auto-pair ran into an issue +--- !u!1 &4967086677765351014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086677765351015} + - component: {fileID: 4967086677765351013} + - component: {fileID: 4967086677765351012} + - component: {fileID: 235867154863528169} + - component: {fileID: 7034300310699233304} + m_Layer: 5 + m_Name: ConnectionDialog + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &4967086677765351015 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677765351014} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3751191164850618611} + - {fileID: 3751191164850618608} + - {fileID: 8857299115396528434} + m_Father: {fileID: 3751191164850618615} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 8, y: 160.00003} + m_SizeDelta: {x: 603.4334, y: 152.50421} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086677765351013 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677765351014} + m_CullTransparentMesh: 0 +--- !u!114 &4967086677765351012 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677765351014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.039215688, g: 0.039215688, b: 0.039215688, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &235867154863528169 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677765351014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4967086677765351012} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &7034300310699233304 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086677765351014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: bb1cc47c374f478e861f2c3dade07e1a, type: 3} + m_Name: + m_EditorClassIdentifier: + buttonHide: {fileID: 235867154863528169} + textSummary: {fileID: 3751191164850618565} + textSuggestion: {fileID: 462332990179851889} + pendingPatches: 0 + patchesApplied: 0 +--- !u!1 &4967086678334773011 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086678334773008} + - component: {fileID: 4967086678334773014} + - component: {fileID: 4967086678334773009} + - component: {fileID: 3727107046497244783} + m_Layer: 5 + m_Name: RetryDialog + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &4967086678334773008 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678334773011} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4967086676871555596} + - {fileID: 4967086677533727707} + - {fileID: 6280529082113425347} + - {fileID: 4967086678661718217} + - {fileID: 4967086677112779038} + - {fileID: 4911193491485015256} + - {fileID: 6235104375140882572} + - {fileID: 8150310283045374484} + m_Father: {fileID: 3751191164850618615} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 8, y: 160} + m_SizeDelta: {x: 603.4334, y: 203.0878} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086678334773014 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678334773011} + m_CullTransparentMesh: 0 +--- !u!114 &4967086678334773009 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678334773011} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.039215688, g: 0.039215688, b: 0.039215688, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &3727107046497244783 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678334773011} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7a69f8e8e50a405a84ec22ac7c2f4bdc, type: 3} + m_Name: + m_EditorClassIdentifier: + buttonHide: {fileID: 4967086678661718222} + buttonRetryAutoPair: {fileID: 4967086677112779039} + buttonTroubleshoot: {fileID: 6672458751395352801} + textSummary: {fileID: 4967086677533727704} + textSuggestion: {fileID: 116564040413298098} + ipInput: {fileID: 7429817927027686359} + textForDebugging: {fileID: 6495855994796430067} + enableDebugging: 0 +--- !u!1 &4967086678661718216 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4967086678661718217} + - component: {fileID: 4967086678661718220} + - component: {fileID: 4967086678661718223} + - component: {fileID: 4967086678661718222} + m_Layer: 5 + m_Name: ButtonHide + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4967086678661718217 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678661718216} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.8, y: 0.8, z: 0.5} + m_Children: + - {fileID: 3751191164850618614} + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -45.7, y: -26} + m_SizeDelta: {x: 95.76041, y: 46.033897} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4967086678661718220 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678661718216} + m_CullTransparentMesh: 0 +--- !u!114 &4967086678661718223 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678661718216} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.745283, g: 0.745283, b: 0.745283, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4967086678661718222 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4967086678661718216} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4967086678661718223} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &5487644203504871490 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4911193491485015256} + - component: {fileID: 3977274743991914834} + - component: {fileID: 2696062604266108078} + - component: {fileID: 6672458751395352801} + m_Layer: 5 + m_Name: ButtonTroubleshoot + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4911193491485015256 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5487644203504871490} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4216036513099635638} + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: 200.54193, y: 34.638} + m_SizeDelta: {x: -447.021, y: 45.0679} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3977274743991914834 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5487644203504871490} + m_CullTransparentMesh: 0 +--- !u!114 &2696062604266108078 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5487644203504871490} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &6672458751395352801 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5487644203504871490} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2696062604266108078} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &6563246299181214611 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6484505723585156786} + - component: {fileID: 1661502203157216626} + - component: {fileID: 5891534192019788270} + - component: {fileID: 2310985356733911194} + m_Layer: 5 + m_Name: ReusedQuestionDialog + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &6484505723585156786 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6563246299181214611} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4288889589057652595} + - {fileID: 5505278863775282652} + - {fileID: 6528462525361087078} + - {fileID: 8361365728969909008} + - {fileID: 7107734678944665722} + - {fileID: 8252921096633957241} + m_Father: {fileID: 3751191164850618615} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 8, y: 160} + m_SizeDelta: {x: 603.4334, y: 203.0878} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1661502203157216626 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6563246299181214611} + m_CullTransparentMesh: 0 +--- !u!114 &5891534192019788270 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6563246299181214611} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.039215688, g: 0.039215688, b: 0.039215688, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &2310985356733911194 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6563246299181214611} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ef31038a0ed84685b779466bf22d53a9, type: 3} + m_Name: + m_EditorClassIdentifier: + textSummary: {fileID: 6369210938302316831} + textSuggestion: {fileID: 5675038352245823804} + buttonContinue: {fileID: 5265040605375167127} + buttonCancel: {fileID: 5585168207715079851} + buttonMoreInfo: {fileID: 3158748587153539730} +--- !u!1 &6697092821899816264 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5758986847798381115} + - component: {fileID: 5463465806778337131} + - component: {fileID: 8072588694671228428} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5758986847798381115 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6697092821899816264} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 8252921096633957241} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5463465806778337131 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6697092821899816264} + m_CullTransparentMesh: 0 +--- !u!114 &8072588694671228428 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6697092821899816264} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.11320752, g: 0.11320752, b: 0.11320752, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 20 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: More Info +--- !u!1 &7506156204490477245 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7107734678944665722} + - component: {fileID: 677914236431176414} + - component: {fileID: 9047087872360317623} + - component: {fileID: 5265040605375167127} + m_Layer: 5 + m_Name: ButtonContinue + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7107734678944665722 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7506156204490477245} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1221019002237951643} + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: -261.41, y: -64.89999} + m_SizeDelta: {x: 141.6914, y: 45.0679} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &677914236431176414 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7506156204490477245} + m_CullTransparentMesh: 0 +--- !u!114 &9047087872360317623 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7506156204490477245} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &5265040605375167127 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7506156204490477245} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Highlighted + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 9047087872360317623} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &7769261099572506218 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8150310283045374484} + - component: {fileID: 5348576082586449480} + - component: {fileID: 6647422777320841469} + - component: {fileID: 7429817927027686359} + m_Layer: 5 + m_Name: IpInput + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8150310283045374484 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769261099572506218} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8207283617714762719} + - {fileID: 8141605075363586685} + m_Father: {fileID: 4967086678334773008} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: -125.62808, y: -66.75602} + m_SizeDelta: {x: 180.7402, y: 41.1} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5348576082586449480 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769261099572506218} + m_CullTransparentMesh: 1 +--- !u!114 &6647422777320841469 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769261099572506218} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &7429817927027686359 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7769261099572506218} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 6647422777320841469} + m_TextComponent: {fileID: 3409363427364332004} + m_Placeholder: {fileID: 8746367729340876900} + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 0 + m_HideMobileInput: 0 + m_CharacterValidation: 0 + m_CharacterLimit: 21 + m_OnEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 +--- !u!1 &8054601594198067103 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8564535462043123833} + - component: {fileID: 7768483217031697610} + - component: {fileID: 1047141224289122821} + m_Layer: 5 + m_Name: FallbackInputProvider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &8564535462043123833 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8054601594198067103} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4967086677379066171} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &7768483217031697610 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8054601594198067103} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!114 &1047141224289122821 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8054601594198067103} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!1 &8201367103125407330 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8207283617714762719} + - component: {fileID: 2716239957931866459} + - component: {fileID: 8746367729340876900} + m_Layer: 5 + m_Name: Placeholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8207283617714762719 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8201367103125407330} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 8150310283045374484} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2716239957931866459 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8201367103125407330} + m_CullTransparentMesh: 1 +--- !u!114 &8746367729340876900 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8201367103125407330} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 2 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Enter text... +--- !u!1 &9029651609518542122 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4288889589057652595} + - component: {fileID: 1714143093284739457} + - component: {fileID: 3128017247211677084} + m_Layer: 5 + m_Name: ImageLogo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4288889589057652595 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9029651609518542122} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6484505723585156786} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 42.06, y: -39.16} + m_SizeDelta: {x: 54.687653, y: 54.687653} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1714143093284739457 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9029651609518542122} + m_CullTransparentMesh: 0 +--- !u!114 &3128017247211677084 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9029651609518542122} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 90cf8e542151548c6aa3cba26467e144, type: 3} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab.meta b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab.meta new file mode 100644 index 0000000..8313f8d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0dc8d7047b14c44b7970c5d35665dbe1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs new file mode 100644 index 0000000..b4f837b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using System.Linq; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace SingularityGroup.HotReload { + + internal static class HotReloadSettingsHelper { + public static UnityEngine.GameObject GetOrCreateSettingsPrefab(string prefabAssetPath) { + #if UNITY_EDITOR + var prefab = AssetDatabase.LoadAssetAtPath(prefabAssetPath); + if (prefab == null) { + // when you use HotReload as a unitypackage, prefab is somewhere inside your assets folder + var guids = AssetDatabase.FindAssets("HotReloadPrompts t:prefab", new string[]{"Assets"}); + var paths = guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)); + var promptsPrefabPath = paths.FirstOrDefault(assetpath => Path.GetFileName(assetpath) == "HotReloadPrompts.prefab"); + if (promptsPrefabPath != null) { + prefab = AssetDatabase.LoadAssetAtPath(promptsPrefabPath); + } + } + if (prefab == null) { + throw new Exception(Localization.Translations.Errors.FailedPromptsPrefab); + } + return prefab; + #else + return null; + #endif + } + + public static HotReloadSettingsObject GetSettingsObject(string editorAssetPath) { + #if UNITY_EDITOR + return AssetDatabase.LoadAssetAtPath(editorAssetPath); + #else + return null; + #endif + } + + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs.meta new file mode 100644 index 0000000..7dc9408 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 4bff36678f3d4a2bb24c477d28f96888 +timeCreated: 1765129558 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs new file mode 100644 index 0000000..b94e469 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs @@ -0,0 +1,112 @@ +using System; +using JetBrains.Annotations; +using System.IO; +using UnityEngine; + +namespace SingularityGroup.HotReload { + /// + /// HotReload runtime settings. These can be changed while the app is running. + /// + /// + /// ScriptableObject that may be included in Resources/ folder. + /// See also Editor/PrebuildIncludeResources.cs + /// + [Serializable] + class HotReloadSettingsObject : ScriptableObject { + #region singleton + private static HotReloadSettingsObject _I; + public static HotReloadSettingsObject I { + get { + if (_I == null) { + _I = LoadSettingsOrDefault(); + } + return _I; + } + } + + /// Create settings inside Assets/ because user cannot edit files that are included inside a Unity package + /// + /// You can change this in a build script if you want it created somewhere else. + /// + public static string editorAssetPath = "Assets/HotReload/Resources/HotReloadSettingsObject.asset"; + + private static string resourceName => Path.GetFileNameWithoutExtension(editorAssetPath); + + public static bool TryLoadSettings(out HotReloadSettingsObject settings) { + try { + settings = LoadSettings(); + return settings != null; + } catch(FileNotFoundException) { + settings = null; + return false; + } + } + + [NotNull] + private static HotReloadSettingsObject LoadSettingsOrDefault() { + var settings = LoadSettings(); + if (settings == null) { + // load defaults + settings = CreateInstance(); + } + return settings; + } + + [CanBeNull] + private static HotReloadSettingsObject LoadSettings() { + HotReloadSettingsObject settings; + if (Application.isEditor) { + settings = HotReloadSettingsHelper.GetSettingsObject(editorAssetPath); + } else { + // load from Resources (assumes that build includes the resource) + settings = Resources.Load(resourceName); + } + return settings; + } + #endregion + + #region settings + + /// Set default values. + /// + /// This is called by the Unity editor when the ScriptableObject is first created. + /// This function is only called in editor mode. + /// + private void Reset() { + EnsurePrefabSetCorrectly(); + } + + /// + /// Path to the prefab asset file. + /// + const string prefabAssetPath = "Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab"; + + // Call this during build, just to be sure the field is correct. (I had some issues with it while editing the prefab) + public void EnsurePrefabSetCorrectly() { + PromptsPrefab = HotReloadSettingsHelper.GetOrCreateSettingsPrefab(prefabAssetPath); + } + + public void EnsurePrefabNotInBuild() { + PromptsPrefab = null; + } + + + // put the stored settings here + + [Header(Localization.Translations.MenuItems.BuildSettings)] + [Tooltip(Localization.Translations.MenuItems.IncludeInBuildTooltip)] + public bool IncludeInBuild = true; + + [Header(Localization.Translations.MenuItems.PlayerSettings)] + public bool AllowAndroidAppToMakeHttpRequests = false; + + #region hidden + + /// Reference to the Prefab, for loading it at runtime + [HideInInspector] + public GameObject PromptsPrefab; + #endregion + + #endregion settings + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs.meta new file mode 100644 index 0000000..887f361 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 324c6fd3c103e0f418eb4b98c46bf63c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - PromptsPrefab: {fileID: 4967086677379066170, guid: 0dc8d7047b14c44b7970c5d35665dbe1, + type: 3} + 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/Runtime/HotReloadSettingsObject.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs new file mode 100644 index 0000000..0dc3779 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs @@ -0,0 +1,16 @@ +using System.Net.Http; + +namespace SingularityGroup.HotReload { + + public class HttpClientUtils { + public static HttpClient CreateHttpClient() { + var handler = new HttpClientHandler { + // Without this flag HttpClients don't work for PCs with double-byte characters in the name + UseCookies = false + }; + + return new HttpClient(handler); + } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs.meta new file mode 100644 index 0000000..dcda117 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b40f5d8cac104565b0aaa1d1e294ff8f +timeCreated: 1700069330 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs new file mode 100644 index 0000000..a6915fe --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs @@ -0,0 +1,9 @@ +namespace SingularityGroup.HotReload { + public interface IServerHealthCheck { + bool IsServerHealthy { get; } + } + + internal interface IServerHealthCheckInternal : IServerHealthCheck { + void CheckHealth(); + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs.meta new file mode 100644 index 0000000..62e9191 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bcb0ff221290427182643b815685ea97 +timeCreated: 1675232020 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs new file mode 100644 index 0000000..8b60cbc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + class InstallQRDialog : MonoBehaviour { + public Button buttonGo; + public Button buttonHide; + + private void Start() { + buttonHide.onClick.AddListener(Hide); + + // launch camera app that can scan QR-Code https://singularitygroup.atlassian.net/browse/SG-29495 + buttonGo.onClick.AddListener(() => { + Hide(); + var recommendedQrCodeApp = "com.scanteam.qrcodereader"; + Application.OpenURL($"https://play.google.com/store/apps/details?id={recommendedQrCodeApp}"); + }); + } + + /// hide this dialog + void Hide() { + gameObject.SetActive(false); // this should disable the Update loop? + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs.meta new file mode 100644 index 0000000..079ae57 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 03d3be3b485a4450b112f9ea3af4fb66 +timeCreated: 1674988075 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs new file mode 100644 index 0000000..685a3fa --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs @@ -0,0 +1,62 @@ +#if UNITY_ANDROID && !UNITY_EDITOR +#define MOBILE_ANDROID +#endif +#if UNITY_IOS && !UNITY_EDITOR +#define MOBILE_IOS +#endif +#if MOBILE_ANDROID || MOBILE_IOS +#define MOBILE +#endif + +using System; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace SingularityGroup.HotReload { + static class IpHelper { + // get my local ip address + + static DateTime cachedAt; + static string ipCached; + public static string GetIpAddressCached() { + if (string.IsNullOrEmpty(ipCached) || DateTime.UtcNow - cachedAt > TimeSpan.FromSeconds(5)) { + ipCached = GetIpAddress(); + cachedAt = DateTime.UtcNow; + } + return ipCached; + } + + public static string GetIpAddress() { + var ip = GetLocalIPv4(NetworkInterfaceType.Wireless80211); + + if (string.IsNullOrEmpty(ip)) { + return GetLocalIPv4(NetworkInterfaceType.Ethernet); + } + return ip; + } + + private static string GetLocalIPv4(NetworkInterfaceType _type) { + string output = ""; + foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces()) { + if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up) { + foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses) { + if (ip.Address.AddressFamily == AddressFamily.InterNetwork && IsLocalIp(ip.Address.MapToIPv4().GetAddressBytes())) { + output = ip.Address.ToString(); + } + } + } + } + return output; + } + + // https://datatracker.ietf.org/doc/html/rfc1918#section-3 + static bool IsLocalIp(byte[] ipAddress) { + return ipAddress[0] == 10 + || ipAddress[0] == 172 + && ipAddress[1] >= 16 + && ipAddress[1] <= 31 + || ipAddress[0] == 192 + && ipAddress[1] == 168; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs.meta new file mode 100644 index 0000000..7409e29 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 4d3a24a25ced4eae8b7e0b9b5a0d5c9d +timeCreated: 1674145172 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs.meta new file mode 100644 index 0000000..192e762 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 053fc5684eb47f54e8c877cb1ade54d6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly.meta new file mode 100644 index 0000000..d961c60 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 520640393141aab41bd6d6b1f43e7037 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll new file mode 100644 index 0000000..48765ef Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll.meta new file mode 100644 index 0000000..adbdd06 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll.meta @@ -0,0 +1,99 @@ +fileFormatVersion: 2 +guid: e0277ee5c436c344a9d7720bdc0391d1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + WebGL: WebGL + second: + enabled: 0 + settings: {} + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll new file mode 100644 index 0000000..7069e00 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta new file mode 100644 index 0000000..dc4d5c7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 49b66a954ad81dd4795e880bd63dc4c3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - UNITY_2019_4_OR_NEWER + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll new file mode 100644 index 0000000..dcf44db Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta new file mode 100644 index 0000000..cf60d68 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 784812c918589424a90509ea34a51da0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - UNITY_2020_3_OR_NEWER + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll new file mode 100644 index 0000000..c0d33b3 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta new file mode 100644 index 0000000..15f2ed0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 8c8658e0b34ecf04ca6ca07ffa6fc846 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - UNITY_2022_2_OR_NEWER + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice.meta new file mode 100644 index 0000000..5c91b3f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b3d6360d6d1f2c47b659f0a4960ebfe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll new file mode 100644 index 0000000..48765ef Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll.meta new file mode 100644 index 0000000..3ca55df --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 15528e9db0a6c9b45a66378f0b6c4dd6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - ENABLE_MONO + - DEVELOPMENT_BUILD || UNITY_EDITOR + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll new file mode 100644 index 0000000..7069e00 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta new file mode 100644 index 0000000..d5f7e31 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll.meta @@ -0,0 +1,96 @@ +fileFormatVersion: 2 +guid: 4febf8334e6a82f4e9faf3513c7fcc8d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - ENABLE_MONO + - UNITY_2019_4_OR_NEWER + - DEVELOPMENT_BUILD || UNITY_EDITOR + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll new file mode 100644 index 0000000..dcf44db Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta new file mode 100644 index 0000000..099163d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll.meta @@ -0,0 +1,96 @@ +fileFormatVersion: 2 +guid: 35c3cec01c5230e41bea51d3ac6fcfa1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - ENABLE_MONO + - UNITY_2020_3_OR_NEWER + - DEVELOPMENT_BUILD || UNITY_EDITOR + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll new file mode 100644 index 0000000..c0d33b3 Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta new file mode 100644 index 0000000..4660d74 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll.meta @@ -0,0 +1,96 @@ +fileFormatVersion: 2 +guid: c9f10603236554c4896f310072d57f24 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: + - ENABLE_MONO + - UNITY_2022_2_OR_NEWER + - DEVELOPMENT_BUILD || UNITY_EDITOR + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization.meta new file mode 100644 index 0000000..0d1fb84 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 392d4a942d5d4d26872be33e375c8d32 +timeCreated: 1759652490 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs new file mode 100644 index 0000000..f6257ed --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs @@ -0,0 +1,46 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class Common { + public static string UnknownException; + public static string HotReloadIsRunning; + public static string HotReloadIsNotRunning; + public static string UnableToResolveMethod; + public static string UnableToResolveType; + public static string UnableToResolveField; + public static string HotReloadUnreachable; + public static string TryingToReconnect; + public static string Disconnected; + public static string Unknown; + + public static void LoadEnglish() { + UnknownException = "unknown exception"; + HotReloadIsRunning = "Hot Reload is running"; + HotReloadIsNotRunning = "Hot Reload is not running"; + UnableToResolveMethod = "Unable to resolve method"; + UnableToResolveType = "Unable to resolve type"; + UnableToResolveField = "Unable to resolve field"; + HotReloadUnreachable = "Hot Reload was unreachable for 5 seconds, trying to reconnect..."; + TryingToReconnect = "Trying to reconnect..."; + Disconnected = "Disconnected"; + Unknown = "unknown"; + } + + public static void LoadSimplifiedChinese() { + UnknownException = "未知异常"; + HotReloadIsRunning = "Hot Reload 正在运行"; + HotReloadIsNotRunning = "Hot Reload 未运行"; + UnableToResolveMethod = "无法解析方法"; + UnableToResolveType = "无法解析类型"; + UnableToResolveField = "无法解析字段"; + HotReloadUnreachable = "Hot Reload 5 秒内无法访问,正在尝试重新连接..."; + TryingToReconnect = "正在尝试重新连接..."; + Disconnected = "已断开连接"; + Unknown = "未知"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs.meta new file mode 100644 index 0000000..a756b0a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 71c2ede153664ef48b2919d3c42da1a3 +timeCreated: 1762538401 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/CommonTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs new file mode 100644 index 0000000..7cf1850 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs @@ -0,0 +1,82 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class Dialogs { + public static string Information; + public static string ContinueButtonText; + public static string CancelButtonText; + public static string DifferentProjectSummary; + public static string DifferentProjectSuggestion; + public static string DifferentCommitSummary; + public static string DifferentCommitSuggestion; + public static string ConnectionStateConnecting; + public static string ConnectionStateHandshaking; + public static string ConnectionStateDifferencesFound; + public static string ConnectionStateConnected; + public static string ConnectionStateCancelled; + public static string Patches; + public static string IsConnected; + public static string NoWiFiNetwork; + public static string WaitForCompiling; + public static string TargetNetworkIsReachable; + public static string AutoPairEncounteredIssue; + public static string ConnectionFailed; + public static string TryingToReconnect; + public static string Disconnected; + public static string PatchesStatus; + + public static void LoadEnglish() { + Information = "Information"; + ContinueButtonText = "Continue"; + CancelButtonText = "Cancel"; + DifferentProjectSummary = "Hot Reload was started from a different project"; + DifferentProjectSuggestion = "Please run Hot Reload from the matching Unity project"; + DifferentCommitSummary = "Editor and current build are on different commits"; + DifferentCommitSuggestion = "This can cause errors when the build was made on an old commit."; + ConnectionStateConnecting = "Connecting ..."; + ConnectionStateHandshaking = "Handshaking ..."; + ConnectionStateDifferencesFound = "Differences found"; + ConnectionStateConnected = "Connected!"; + ConnectionStateCancelled = "Cancelled"; + Patches = "Patches"; + IsConnected = "Is this device connected to {0}?"; + NoWiFiNetwork = "WiFi"; + WaitForCompiling = "Wait for compiling to finish before trying again"; + TargetNetworkIsReachable = "Make sure you're on the same {0} network. Also ensure Hot Reload is running"; + AutoPairEncounteredIssue = "Auto-pair encountered an issue"; + ConnectionFailed = "Connection failed"; + TryingToReconnect = "Trying to reconnect ..."; + Disconnected = "Disconnected"; + PatchesStatus = "Patches: {0} pending, {1} applied"; + } + + public static void LoadSimplifiedChinese() { + Information = "信息"; + ContinueButtonText = "继续"; + CancelButtonText = "取消"; + DifferentProjectSummary = "Hot Reload 从不同的项目启动"; + DifferentProjectSuggestion = "请从匹配的 Unity 项目运行 Hot Reload"; + DifferentCommitSummary = "编辑器和当前构建在不同的提交上"; + DifferentCommitSuggestion = "当构建是在旧的提交上进行时,这可能会导致错误。"; + ConnectionStateConnecting = "正在连接 ..."; + ConnectionStateHandshaking = "正在握手 ..."; + ConnectionStateDifferencesFound = "发现差异"; + ConnectionStateConnected = "已连接!"; + ConnectionStateCancelled = "已取消"; + Patches = "补丁"; + IsConnected = "此设备是否已连接到 {0}?"; + NoWiFiNetwork = "WiFi"; + WaitForCompiling = "请等待编译完成后再试"; + TargetNetworkIsReachable = "请确保您在同一个 {0} 网络中。还要确保 Hot Reload 正在运行"; + AutoPairEncounteredIssue = "自动配对遇到问题"; + ConnectionFailed = "连接失败"; + TryingToReconnect = "正在尝试重新连接 ..."; + Disconnected = "已断开连接"; + PatchesStatus = "补丁:{0} 待处理,{1} 已应用"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs.meta new file mode 100644 index 0000000..f254df7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3970cf8b2b8a47dd9d16ce5051375690 +timeCreated: 1762538420 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/DialogTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs new file mode 100644 index 0000000..b33ecfe --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs @@ -0,0 +1,73 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class Errors { + public static string MethodNameMismatch; + public static string DeclaringTypeNameMismatch; + public static string IsGenericMethodDefinitionMismatch; + public static string MissingThisParameter; + public static string ThisParameterTypeMismatch; + public static string ParameterCountMismatch; + public static string ParameterTypeMismatch; + public static string ReturnTypeMismatch; + public static string GenericParameterNotGenericType; + public static string GenericParameterDidNotExist; + public static string IsPlayerWithHotReloadFalse; + public static string UnknownExceptionReadingBuildInfo; + public static string BuildInfoNotFound; + public static string FailedPromptsPrefab; + public static string HandshakeFailedInvalidBuildTarget; + public static string BuildTargetMismatch; + public static string UnableToResolveMethodInAssembly; + public static string UnableToResolveTypeInAssembly; + public static string UnableToResolveFieldInAssembly; + + public static void LoadEnglish() { + MethodNameMismatch = "Method name mismatch"; + DeclaringTypeNameMismatch = "Declaring type name mismatch"; + IsGenericMethodDefinitionMismatch = "IsGenericMethodDefinition mismatch"; + MissingThisParameter = "missing this parameter"; + ThisParameterTypeMismatch = "this parameter type mismatch"; + ParameterCountMismatch = "parameter count mismatch"; + ParameterTypeMismatch = "parameter type mismatch"; + ReturnTypeMismatch = "Return type mismatch"; + GenericParameterNotGenericType = "Generic parameter did not resolve to generic type definition"; + GenericParameterDidNotExist = "Generic parameter did not exist on the generic type definition"; + IsPlayerWithHotReloadFalse = "IsPlayerWithHotReload() is false"; + UnknownExceptionReadingBuildInfo = "Uknown exception happened when reading build info"; + BuildInfoNotFound = "Uknown issue happened when reading build info."; + FailedPromptsPrefab = "Failed to find PromptsPrefab (are you using Hot Reload as a package?"; + HandshakeFailedInvalidBuildTarget = "Server did not declare its current Unity activeBuildTarget in the handshake response. Will assume it is {0}."; + BuildTargetMismatch = "Your Unity project is running on {0}. You may need to switch it to {1} for Hot Reload to work."; + UnableToResolveMethodInAssembly = "Unable to resolve method {0} in assembly {1}"; + UnableToResolveTypeInAssembly = "Unable to resolve type with name: {0} in assembly {1}"; + UnableToResolveFieldInAssembly = "Unable to resolve field with name: {0} in assembly {1}"; + } + + public static void LoadSimplifiedChinese() { + MethodNameMismatch = "方法名称不匹配"; + DeclaringTypeNameMismatch = "声明类型名称不匹配"; + IsGenericMethodDefinitionMismatch = "IsGenericMethodDefinition 不匹配"; + MissingThisParameter = "缺少 this 参数"; + ThisParameterTypeMismatch = "this 参数类型不匹配"; + ParameterCountMismatch = "参数数量不匹配"; + ParameterTypeMismatch = "参数类型不匹配"; + ReturnTypeMismatch = "返回类型不匹配"; + GenericParameterNotGenericType = "泛型参数未解析为泛型类型定义"; + GenericParameterDidNotExist = "泛型参数在泛型类型定义上不存在"; + IsPlayerWithHotReloadFalse = "IsPlayerWithHotReload() 为 false"; + UnknownExceptionReadingBuildInfo = "读取构建信息时发生未知异常"; + BuildInfoNotFound = "读取构建信息时发生未知问题。"; + FailedPromptsPrefab = "未能找到 PromptsPrefab(您是否将 Hot Reload 作为软件包使用?"; + HandshakeFailedInvalidBuildTarget = "服务器在握手响应中未声明其当前的 Unity activeBuildTarget。将假定为 {0}。"; + BuildTargetMismatch = "您的 Unity 项目正在 {0} 上运行。您可能需要将其切换到 {1} 才能使 Hot Reload 工作。"; + UnableToResolveMethodInAssembly = "无法在程序集 {1} 中解析方法 {0}"; + UnableToResolveTypeInAssembly = "无法在程序集 {1} 中解析名称为 {0} 的类型"; + UnableToResolveFieldInAssembly = "无法在程序集 {1} 中解析名称为 {0} 的字段"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs.meta new file mode 100644 index 0000000..2d7087a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c96d2f0898524320839b84cf22fcd820 +timeCreated: 1762538447 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/ErrorTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs new file mode 100644 index 0000000..4d33a01 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs @@ -0,0 +1,178 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class Logging { + // Server and Connection + public static string HotReloadUnreachableDisconnecting; + public static string RequestHandshakeToServer; + public static string ServerHealthyAfterHandshake; + + // Polling Errors + public static string PollMethodPatchesFailed; + public static string PollPatchStatusFailed; + public static string PollAssetChangesFailed; + + // Request Errors + public static string DeserializingResponseFailed; + public static string RequestTimeout; + + // Method Invocation + public static string InvokeOnHotReloadFailed; + public static string InvokeOnHotReloadLocalFailed; + + // Build and Player + public static string HotReloadNotAvailableBuildSettings; + public static string BuildInfoNotFound; + + // Method Compatibility + public static string UnknownIssue; + + // Patch Loading/Saving + public static string LoadingPatchesFromDiskError; + public static string LoadingPatchesFromFile; + public static string LoadedPatchesFromDisk; + public static string SavingAppliedPatches; + + // Patch Registration/Application + public static string RegisterPatches; + public static string ApplyPatchesPending; + public static string DetourMethod; + + // Exceptions + public static string ExceptionHandlingMethodPatch; + public static string ExceptionApplyingPatch; + public static string ExceptionEnsureUnityEventMethod; + public static string ExceptionRemoveUnityEventMethod; + public static string InvalidPath; + + // Field Operations + public static string FailedRegisteringInitializerInvalidMethod; + public static string FailedRegisteringInitializerException; + public static string FailedRegisteringNewFieldDefinitions; + public static string FailedRemovingInitializer; + public static string FailedRemovingFieldValue; + public static string FailedMovingFieldValue; + public static string FailedUpdatingFieldAttributes; + public static string FailedAddingFieldToInspector; + public static string FailedHidingFieldFromInspector; + + // Method Patching + public static string DebuggerAttachedNotAllowed; + public static string MethodMismatch; + public static string FailedToApplyPatchForMethod; + public static string HotReloadApplyTook; + + // Unity Events + public static string SceneLoadedWithNewUnityEventMethods; + + public static void LoadEnglish() { + HotReloadUnreachableDisconnecting = "Hot Reload was unreachable for {0} seconds, disconnecting"; + RequestHandshakeToServer = "Request handshake to Hot Reload server with hostname: {0}"; + ServerHealthyAfterHandshake = "Server is healthy after first handshake? {0}"; + + PollMethodPatchesFailed = "PollMethodPatches failed with code {0} {1} {2}"; + PollPatchStatusFailed = "PollPatchStatus failed with code {0} {1} {2}"; + PollAssetChangesFailed = "PollAssetChanges failed with code {0} {1} {2}"; + + DeserializingResponseFailed = "Deserializing response failed with {0}: {1}"; + RequestTimeout = "Request timeout"; + + InvokeOnHotReloadFailed = "[InvokeOnHotReload] {0} {1} failed. Exception:\n{2}"; + InvokeOnHotReloadLocalFailed = "[InvokeOnHotReloadLocal] {0} {1} failed. Exception:\n{2}"; + + HotReloadNotAvailableBuildSettings = "Hot Reload is not available in this build because one or more build settings were not supported."; + BuildInfoNotFound = "Build info not found"; + + UnknownIssue = "unknown issue"; + + LoadingPatchesFromDiskError = "Encountered exception when loading patches from disk:"; + LoadingPatchesFromFile = "Loading patches from file {0}"; + LoadedPatchesFromDisk = "Loaded {0} patches from disk"; + SavingAppliedPatches = "Saving {0} applied patches to {1}"; + + RegisterPatches = "Register patches.\nWarnings: {0} \nMethods:\n{1}"; + ApplyPatchesPending = "ApplyPatches. {0} patches pending."; + DetourMethod = "Detour method {0:X8} {1}, offset: {2}"; + + ExceptionHandlingMethodPatch = "Exception occured when handling method patch. Exception:"; + ExceptionApplyingPatch = "Edit requires full recompile to apply: Encountered exception when applying a patch.\nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after."; + ExceptionEnsureUnityEventMethod = "Encountered exception in EnsureUnityEventMethod: {0} {1}"; + ExceptionRemoveUnityEventMethod = "Encountered exception in RemoveUnityEventMethod: {0} {1}"; + InvalidPath = "Invalid path: {0}"; + + FailedRegisteringInitializerInvalidMethod = "Failed registering initializer for field {0} in {1}. Field value might not be initialized correctly. Invalid method."; + FailedRegisteringInitializerException = "Failed registering initializer for field {0} in {1}. Field value might not be initialized correctly. Exception: {2}"; + FailedRegisteringNewFieldDefinitions = "Failed registering new field definitions for field {0} in {1}. Exception: {2}"; + FailedRemovingInitializer = "Failed removing initializer for field {0} in {1}. Field value might not be initialized correctly. Exception: {2}"; + FailedRemovingFieldValue = "Failed removing field value from {0} in {1}. Field value in code might not be up to date. Exception: {2}"; + FailedMovingFieldValue = "Failed moving field value from {0} to {1} in {2}. Field value in code might not be up to date. Exception: {3}"; + FailedUpdatingFieldAttributes = "Failed updating field attributes of {0} in {1}. Updates might not reflect in the inspector. Exception: {2}"; + FailedAddingFieldToInspector = "Failed adding field {0}:{1} to the inspector. Field will not be displayed. Exception: {2}"; + FailedHidingFieldFromInspector = "Failed hiding field {0}:{1} from the inspector. Exception: {2}"; + + DebuggerAttachedNotAllowed = "Patching methods is not allowed while the Debugger is attached. You can change this behavior in settings if Hot Reload is compatible with the debugger you're running."; + MethodMismatch = "Edit requires full recompile to apply: Method mismatch: {0}, patch: {1}. \nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after."; + FailedToApplyPatchForMethod = "Edit requires full recompile to apply: Failed to apply patch for method {0} in assembly {1}.\nCommon causes: editing code that failed to patch previously, an unsupported change, or a real bug in Hot Reload.\nIf you think this is a bug, please report the issue on Discord and include a code-snippet before/after.\nException: {2}"; + HotReloadApplyTook = "Hot Reload apply took {0}"; + + SceneLoadedWithNewUnityEventMethods = "A new Scene was loaded while new unity event methods were added at runtime. MonoBehaviours in the Scene will not trigger these new events."; + } + + public static void LoadSimplifiedChinese() { + HotReloadUnreachableDisconnecting = "Hot Reload {0} 秒内无法访问,正在断开连接"; + RequestHandshakeToServer = "向 Hot Reload 服务器请求握手,主机名:{0}"; + ServerHealthyAfterHandshake = "第一次握手后服务器是否健康?{0}"; + + PollMethodPatchesFailed = "PollMethodPatches 失败,代码 {0} {1} {2}"; + PollPatchStatusFailed = "PollPatchStatus 失败,代码 {0} {1} {2}"; + PollAssetChangesFailed = "PollAssetChanges 失败,代码 {0} {1} {2}"; + + DeserializingResponseFailed = "反序列化响应失败,{0}:{1}"; + RequestTimeout = "请求超时"; + + InvokeOnHotReloadFailed = "[InvokeOnHotReload] {0} {1} 失败。异常:\n{2}"; + InvokeOnHotReloadLocalFailed = "[InvokeOnHotReloadLocal] {0} {1} 失败。异常:\n{2}"; + + HotReloadNotAvailableBuildSettings = "由于一个或多个构建设置不受支持,Hot Reload 在此构建中不可用。"; + BuildInfoNotFound = "未找到构建信息"; + + UnknownIssue = "未知问题"; + + LoadingPatchesFromDiskError = "从磁盘加载补丁时遇到异常:"; + LoadingPatchesFromFile = "从文件 {0} 加载补丁"; + LoadedPatchesFromDisk = "从磁盘加载了 {0} 个补丁"; + SavingAppliedPatches = "将 {0} 个已应用的补丁保存到 {1}"; + + RegisterPatches = "注册补丁。\n警告:{0} \n方法:\n{1}"; + ApplyPatchesPending = "ApplyPatches。{0} 个补丁待处理。"; + DetourMethod = "Detour 方法 {0:X8} {1},偏移量:{2}"; + + ExceptionHandlingMethodPatch = "处理方法补丁时发生异常。异常:"; + ExceptionApplyingPatch = "编辑需要完全重新编译才能应用:应用补丁时遇到异常。\n常见原因:编辑之前修补失败的代码、不支持的更改或 Hot Reload 中的真正错误。\n如果您认为这是一个错误,请在 Discord 上报告问题并附上之前/之后的代码片段。"; + ExceptionEnsureUnityEventMethod = "在 EnsureUnityEventMethod 中遇到异常:{0} {1}"; + ExceptionRemoveUnityEventMethod = "在 RemoveUnityEventMethod 中遇到异常:{0} {1}"; + InvalidPath = "无效路径:{0}"; + + FailedRegisteringInitializerInvalidMethod = "在 {1} 中为字段 {0} 注册初始化程序失败。字段值可能未正确初始化。方法无效。"; + FailedRegisteringInitializerException = "在 {1} 中为字段 {0} 注册初始化程序失败。字段值可能未正确初始化。异常:{2}"; + FailedRegisteringNewFieldDefinitions = "在 {1} 中为字段 {0} 注册新字段定义失败。异常:{2}"; + FailedRemovingInitializer = "在 {1} 中为字段 {0} 删除初始化程序失败。字段值可能未正确初始化。异常:{2}"; + FailedRemovingFieldValue = "从 {1} 中的 {0} 删除字段值失败。代码中的字段值可能不是最新的。异常:{2}"; + FailedMovingFieldValue = "在 {2} 中将字段值从 {0} 移动到 {1} 失败。代码中的字段值可能不是最新的。异常:{3}"; + FailedUpdatingFieldAttributes = "在 {1} 中更新 {0} 的字段属性失败。更新可能不会反映在检查器中。异常:{2}"; + FailedAddingFieldToInspector = "将字段 {0}:{1} 添加到检查器失败。字段将不会显示。异常:{2}"; + FailedHidingFieldFromInspector = "从检查器中隐藏字段 {0}:{1} 失败。异常:{2}"; + + DebuggerAttachedNotAllowed = "附加调试器时不允许修补方法。如果 Hot Reload 与您正在运行的调试器兼容,您可以在设置中更改此行为。"; + MethodMismatch = "编辑需要完全重新编译才能应用:方法不匹配:{0},补丁:{1}。\n常见原因:编辑之前修补失败的代码、不支持的更改或 Hot Reload 中的真正错误。\n如果您认为这是一个错误,请在 Discord 上报告问题并附上之前/之后的代码片段。"; + FailedToApplyPatchForMethod = "编辑需要完全重新编译才能应用:为程序集 {1} 中的方法 {0} 应用补丁失败。\n常见原因:编辑之前修补失败的代码、不支持的更改或 Hot Reload 中的真正错误。\n如果您认为这是一个错误,请在 Discord 上报告问题并附上之前/之后的代码片段。\n异常:{2}"; + HotReloadApplyTook = "Hot Reload 应用耗时 {0}"; + + SceneLoadedWithNewUnityEventMethods = "在运行时添加新的 unity 事件方法时加载了新场景。场景中的 MonoBehaviours 不会触发这些新事件。"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs.meta new file mode 100644 index 0000000..276e9f7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/LoggingTranslations.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bbabe74466b6cb84bb5d2e98d9779397 +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/Runtime/Localization/LoggingTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs new file mode 100644 index 0000000..c0dceb4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs @@ -0,0 +1,16 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class MenuItems { + public const string UIControls = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "UI 控件" : "UI controls"; + public const string Information = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "信息" : "Information"; + public const string Other = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "其他" : "Other"; + public const string FalllbackEventSystem = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "当项目未能及早创建 EventSystem 时使用" : "Used when project does not create an EventSystem early enough"; + public const string BuildSettings = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "构建设置" : "Build Settings"; + public const string IncludeInBuildTooltip = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "Hot Reload 运行时是否应包含在开发版本中?HotReload 永远不会包含在发布版本中。" : "Should the Hot Reload runtime be included in development builds? HotReload is never included in release builds."; + public const string PlayerSettings = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? "播放器设置" : "Player Settings"; + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs.meta new file mode 100644 index 0000000..314eade --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 12be1a0b0b06402da21c46ae29d60746 +timeCreated: 1762675925 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/MenuItemTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs new file mode 100644 index 0000000..9778e9c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs @@ -0,0 +1,34 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + + internal static partial class Translations { + public static class Settings { + public static string BuildSettings; + public static string IncludeInBuildTooltip; + public static string PlayerSettings; + public static string Other; + public static string FallbackEventSystemTooltip; + public static string NoEventSystemWarning; + + public static void LoadEnglish() { + BuildSettings = "Build Settings"; + IncludeInBuildTooltip = "Should the Hot Reload runtime be included in development builds? HotReload is never included in release builds."; + PlayerSettings = "Player Settings"; + Other = "Other"; + FallbackEventSystemTooltip = "Used when project does not create an EventSystem early enough"; + NoEventSystemWarning = "No EventSystem is active, enabling an EventSystem inside Hot Reload {0} prefab. A Unity EventSystem and an Input module is required for tapping buttons on the Unity UI."; + } + + public static void LoadSimplifiedChinese() { + BuildSettings = "构建设置"; + IncludeInBuildTooltip = "Hot Reload 运行时是否应包含在开发版本中?HotReload 永远不会包含在发布版本中。"; + PlayerSettings = "播放器设置"; + Other = "其他"; + FallbackEventSystemTooltip = "当项目未能及早创建 EventSystem 时使用"; + NoEventSystemWarning = "没有活动的 EventSystem,正在 Hot Reload {0} 预制件内启用 EventSystem。点击 Unity UI 上的按钮需要 Unity EventSystem 和输入模块。"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs.meta new file mode 100644 index 0000000..0cf9f8b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0fe4b08bd0d64689be16dc995c89bf1a +timeCreated: 1762538464 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/SettingTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs new file mode 100644 index 0000000..552dc51 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs @@ -0,0 +1,51 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) + +namespace SingularityGroup.HotReload.Localization { + public static class Locale { + public const string SimplifiedChinese = "zh"; + public const string English = "en"; + } + + 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; + } + + public static void LoadEnglish() { + // Load strings from subclasses + Common.LoadEnglish(); + Dialogs.LoadEnglish(); + Errors.LoadEnglish(); + Settings.LoadEnglish(); + Logging.LoadEnglish(); + Utility.LoadSimplifiedChinese(); + } + + static void LoadSimplifiedChinese() { + Common.LoadSimplifiedChinese(); + Dialogs.LoadSimplifiedChinese(); + Errors.LoadSimplifiedChinese(); + Settings.LoadSimplifiedChinese(); + Logging.LoadSimplifiedChinese(); + Utility.LoadSimplifiedChinese(); + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs.meta new file mode 100644 index 0000000..15604a9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 25df321785e144999ae89a3396247f3d +timeCreated: 1759652512 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/Translations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs b/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs new file mode 100644 index 0000000..5e0b385 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs @@ -0,0 +1,45 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload.Localization { + internal static partial class Translations { + public static class Utility { + public static string BuildSettings; + public static string IncludeInBuildTooltip; + public static string PlayerSettings; + public static string Other; + public static string FallbackEventSystemTooltip; + public static string NoEventSystemWarning; + public static string OnHotReloadWarning; + public static string MethodCallWarning; + public static string OnHotReloadLocalCallWarning; + public static string OnHotReloadLocalWarning; + + public static void LoadEnglish() { + BuildSettings = "Build Settings"; + IncludeInBuildTooltip = "Should the Hot Reload runtime be included in development builds? HotReload is never included in release builds."; + PlayerSettings = "Player Settings"; + Other = "Other"; + FallbackEventSystemTooltip = "Used when project does not create an EventSystem early enough"; + NoEventSystemWarning = "No EventSystem is active, enabling an EventSystem inside Hot Reload {0} prefab. A Unity EventSystem and an Input module is required for tapping buttons on the Unity UI."; + OnHotReloadWarning = "failed. Make sure it has 0 parameters, or 1 parameter with type List. Exception:"; + MethodCallWarning = "failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour."; + OnHotReloadLocalCallWarning = "failed. Make sure it has 0 parameters. Exception:"; + OnHotReloadLocalWarning = "failed to find method {0}. Make sure it exists within the same class."; + } + + public static void LoadSimplifiedChinese() { + BuildSettings = "构建设置"; + IncludeInBuildTooltip = "Hot Reload 运行时是否应包含在开发版本中?HotReload 永远不会包含在发布版本中。"; + PlayerSettings = "播放器设置"; + Other = "其他"; + FallbackEventSystemTooltip = "当项目未能及早创建 EventSystem 时使用"; + NoEventSystemWarning = "没有活动的 EventSystem,正在 Hot Reload {0} 预制件内启用 EventSystem。点击 Unity UI 上的按钮需要 Unity EventSystem 和输入模块。"; + OnHotReloadWarning = "失败。请确保它有 0 个参数,或 1 个类型为 List 的参数。异常:"; + MethodCallWarning = "失败。请确保它是一个具有 0 个参数的方法,静态或在 MonoBehaviour 上定义。"; + OnHotReloadLocalCallWarning = "失败。请确保它有 0 个参数。异常:"; + OnHotReloadLocalWarning = "未能找到方法 {0}。请确保它存在于同一个类中。"; + } + } + } + +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs.meta new file mode 100644 index 0000000..d6acef0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 25823cb7b37e421ca5119f326f3e1b21 +timeCreated: 1762675217 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Localization/UtilityTranslations.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs new file mode 100644 index 0000000..a3d6067 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs @@ -0,0 +1,114 @@ +using System; +using System.Reflection; +using SingularityGroup.HotReload.MonoMod.Utils; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + static class MethodCompatiblity { + internal static string CheckCompatibility(MethodBase previousMethod, MethodBase patchMethod) { + var previousConstructor = previousMethod as ConstructorInfo; + var patchConstructor = patchMethod as ConstructorInfo; + if(previousConstructor != null && !ReferenceEquals(patchConstructor, null)) { + return AreConstructorsCompatible(previousConstructor, patchConstructor); + } + var previousMethodInfo = previousMethod as MethodInfo; + var patchMethodInfo = patchMethod as MethodInfo; + if(!ReferenceEquals(previousMethodInfo, null) && !ReferenceEquals(patchMethodInfo, null)) { + return AreMethodInfosCompatible(previousMethodInfo, patchMethodInfo); + } + return Localization.Translations.Logging.UnknownIssue; + } + + static string AreMethodBasesCompatible(MethodBase previousMethod, MethodBase patchMethod) { + if(previousMethod.Name != patchMethod.Name) { + return Localization.Translations.Errors.MethodNameMismatch; + } + //Declaring type of patch method is different from the target method but their full name (namespace + name) is equal + bool isDeclaringTypeCompatible = false; + var declaringType = patchMethod.DeclaringType; + while (declaringType != null) { + if(previousMethod.DeclaringType?.FullName == declaringType.FullName) { + isDeclaringTypeCompatible = true; + break; + } + declaringType = declaringType.BaseType; + } + if (!isDeclaringTypeCompatible) { + return Localization.Translations.Errors.DeclaringTypeNameMismatch; + } + //Check in case type parameter overloads to distinguish between: void M() { } <-> void M() { } + if(previousMethod.IsGenericMethodDefinition != patchMethod.IsGenericMethodDefinition) { + return Localization.Translations.Errors.IsGenericMethodDefinitionMismatch; + } + + var prevParams = previousMethod.GetParameters(); + var patchParams = patchMethod.GetParameters(); + ArraySegment patchParamsSegment; + bool patchMethodHasExplicitThis; + if(previousMethod.IsStatic || previousMethod.Name.Contains("<") && !patchMethod.IsStatic) { + patchMethodHasExplicitThis = false; + } else { + patchMethodHasExplicitThis = true; + } + if(LikelyHasExplicitThis(prevParams, patchParams, previousMethod)) { + patchMethodHasExplicitThis = true; + } + //Special edge case: User added static keyword to method. No explicit this will be generated in that case + if(!previousMethod.IsStatic && patchMethod.IsStatic && !LikelyHasExplicitThis(prevParams, patchParams, previousMethod)) { + patchMethodHasExplicitThis = false; + } + if(patchMethodHasExplicitThis) { + //Special case: patch method for an instance method is static and has an explicit this parameter. + //If the patch method doesn't have any parameters it is not compatible. + if(patchParams.Length == 0) { + return Localization.Translations.Errors.MissingThisParameter; + } + //this parameter has to be the declaring type + if(!ParamTypeMatches(patchParams[0].ParameterType, previousMethod.DeclaringType)) { + return Localization.Translations.Errors.ThisParameterTypeMismatch; + } + //Ignore the this parameter and compare the remaining ones. + patchParamsSegment = new ArraySegment(patchParams, 1, patchParams.Length - 1); + } else { + patchParamsSegment = new ArraySegment(patchParams); + } + return CompareParameters(new ArraySegment(prevParams), patchParamsSegment); + } + + static bool LikelyHasExplicitThis(ParameterInfo[] prevParams, ParameterInfo[] patchParams, MethodBase previousMethod) { + if (patchParams.Length != prevParams.Length + 1) { + return false; + } + var patchT = patchParams[0].ParameterType; + if (!ParamTypeMatches(patchT, previousMethod.DeclaringType)) { + return false; + } + return patchParams[0].Name == "this"; + } + + static bool ParamTypeMatches(Type patchT, Type originalT) { + return patchT == originalT || patchT.IsByRef && patchT.GetElementType() == originalT; + } + + static string CompareParameters(ArraySegment x, ArraySegment y) { + if(x.Count != y.Count) { + return Localization.Translations.Errors.ParameterCountMismatch; + } + for (var i = 0; i < x.Count; i++) { + if(x.Array[i + x.Offset].ParameterType != y.Array[i + y.Offset].ParameterType) { + return Localization.Translations.Errors.ParameterTypeMismatch; + } + } + return null; + } + + + static string AreConstructorsCompatible(ConstructorInfo x, ConstructorInfo y) { + return AreMethodBasesCompatible(x, y); + } + + static string AreMethodInfosCompatible(MethodInfo x, MethodInfo y) { + return AreMethodBasesCompatible(x, y) ?? (x.ReturnType == y.ReturnType ? null : Localization.Translations.Errors.ReturnTypeMismatch); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs.meta new file mode 100644 index 0000000..46cf9ce --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d731e763662b98941bb06ffc6994a9a8 +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/Runtime/MethodCompatiblity.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs new file mode 100644 index 0000000..2ff6c28 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs @@ -0,0 +1,713 @@ +using System; +using System.Collections.Generic; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Newtonsoft.Json; + +namespace SingularityGroup.HotReload.JsonConverters { + internal class MethodPatchResponsesConverter : JsonConverter { + public override bool CanConvert(Type objectType) { + return objectType == typeof(List); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { + var list = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.StartObject) { + list.Add(ReadMethodPatchResponse(reader)); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the SMethod list + } + } + + return list; + } + + private MethodPatchResponse ReadMethodPatchResponse(JsonReader reader) { + string id = null; + CodePatch[] patches = null; + string[] failures = null; + SMethod[] removedMethod = null; + SField[] alteredFields = null; + SField[] addedFieldInitializerFields = null; + SMethod[] addedFieldInitializerInitializers = null; + SField[] removedFieldInitializers = null; + SField[] newFieldDefinitions = null; + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(MethodPatchResponse.id): + id = reader.ReadAsString(); + break; + + case nameof(MethodPatchResponse.patches): + patches = ReadPatches(reader); + break; + + case nameof(MethodPatchResponse.failures): + failures = ReadStringArray(reader); + break; + + case nameof(MethodPatchResponse.removedMethod): + removedMethod = ReadSMethodArray(reader); + break; + + case nameof(MethodPatchResponse.alteredFields): + alteredFields = ReadSFields(reader); + break; + + case nameof(MethodPatchResponse.addedFieldInitializerFields): + addedFieldInitializerFields = ReadSFields(reader); + break; + + case nameof(MethodPatchResponse.addedFieldInitializerInitializers): + addedFieldInitializerInitializers = ReadSMethodArray(reader); + break; + + case nameof(MethodPatchResponse.removedFieldInitializers): + removedFieldInitializers = ReadSFields(reader); + break; + + case nameof(MethodPatchResponse.newFieldDefinitions): + newFieldDefinitions = ReadSFields(reader); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new MethodPatchResponse( + id ?? string.Empty, + patches ?? Array.Empty(), + failures ?? Array.Empty(), + removedMethod ?? Array.Empty(), + alteredFields ?? Array.Empty(), + // Note: suggestions don't have to be persisted here + Array.Empty(), + Array.Empty(), + addedFieldInitializerFields ?? Array.Empty(), + addedFieldInitializerInitializers ?? Array.Empty(), + removedFieldInitializers ?? Array.Empty(), + newFieldDefinitions ?? Array.Empty() + ); + } + + private CodePatch[] ReadPatches(JsonReader reader) { + var patches = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndArray) { + break; + } + if (reader.TokenType != JsonToken.StartObject) { + continue; + } + string patchId = null; + string assemblyName = null; + byte[] patchAssembly = null; + byte[] patchPdb = null; + SMethod[] modifiedMethods = null; + SMethod[] patchMethods = null; + SMethod[] newMethods = null; + SUnityJob[] unityJobs = null; + SField[] newFields = null; + SField[] deletedFields = null; + SField[] renamedFieldsFrom = null; + SField[] renamedFieldsTo = null; + SField[] propertyAttributesFieldOriginal = null; + SField[] propertyAttributesFieldUpdated = null; + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(CodePatch.patchId): + patchId = reader.ReadAsString(); + break; + + case nameof(CodePatch.assemblyName): + assemblyName = reader.ReadAsString(); + break; + + case nameof(CodePatch.patchAssembly): + patchAssembly = Convert.FromBase64String(reader.ReadAsString()); + break; + + case nameof(CodePatch.patchPdb): + patchPdb = Convert.FromBase64String(reader.ReadAsString()); + break; + + case nameof(CodePatch.modifiedMethods): + modifiedMethods = ReadSMethodArray(reader); + break; + + case nameof(CodePatch.patchMethods): + patchMethods = ReadSMethodArray(reader); + break; + + case nameof(CodePatch.newMethods): + newMethods = ReadSMethodArray(reader); + break; + + case nameof(CodePatch.unityJobs): + unityJobs = ReadSUnityJobArray(reader); + break; + + case nameof(CodePatch.newFields): + newFields = ReadSFields(reader); + break; + case nameof(CodePatch.deletedFields): + deletedFields = ReadSFields(reader); + break; + + case nameof(CodePatch.renamedFieldsFrom): + renamedFieldsFrom = ReadSFields(reader); + break; + + case nameof(CodePatch.renamedFieldsTo): + renamedFieldsTo = ReadSFields(reader); + break; + + case nameof(CodePatch.propertyAttributesFieldOriginal): + propertyAttributesFieldOriginal = ReadSFields(reader); + break; + + case nameof(CodePatch.propertyAttributesFieldUpdated): + propertyAttributesFieldUpdated = ReadSFields(reader); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + patches.Add(new CodePatch( + patchId: patchId ?? string.Empty, + assemblyName: assemblyName ?? string.Empty, + patchAssembly: patchAssembly ?? Array.Empty(), + patchPdb: patchPdb ?? Array.Empty(), + modifiedMethods: modifiedMethods ?? Array.Empty(), + patchMethods: patchMethods ?? Array.Empty(), + newMethods: newMethods ?? Array.Empty(), + unityJobs: unityJobs ?? Array.Empty(), + newFields: newFields ?? Array.Empty(), + deletedFields: deletedFields ?? Array.Empty(), + renamedFieldsFrom: renamedFieldsFrom ?? Array.Empty(), + renamedFieldsTo: renamedFieldsTo ?? Array.Empty(), + propertyAttributesFieldOriginal: propertyAttributesFieldOriginal ?? Array.Empty(), + propertyAttributesFieldUpdated: propertyAttributesFieldUpdated ?? Array.Empty() + )); + } + + return patches.ToArray(); + } + + private string[] ReadStringArray(JsonReader reader) { + var list = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.String) { + list.Add((string)reader.Value); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the string list + } + } + + return list.ToArray(); + } + + private SMethod[] ReadSMethodArray(JsonReader reader) { + var list = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.StartObject) { + list.Add(ReadSMethod(reader)); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the SMethod list + } + } + + return list.ToArray(); + } + + private SType[] ReadSTypeArray(JsonReader reader) { + var list = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.StartObject) { + list.Add(ReadSType(reader)); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the SType list + } + } + + return list.ToArray(); + } + + private SUnityJob[] ReadSUnityJobArray(JsonReader reader) { + var array = new List(); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.StartObject) { + array.Add(ReadSUnityJob(reader)); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the SUnityJob array + } + } + + return array.ToArray(); + } + + private SField[] ReadSFields(JsonReader reader) { + var array = new List(); + while (reader.Read()) { + if (reader.TokenType == JsonToken.StartObject) { + array.Add(ReadSField(reader)); + } else if (reader.TokenType == JsonToken.EndArray) { + break; // End of the SUnityJob array + } + } + return array.ToArray(); + } + + private SMethod ReadSMethod(JsonReader reader) { + string assemblyName = null; + string displayName = null; + int metadataToken = default(int); + string simpleName = null; + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(SMethod.assemblyName): + assemblyName = reader.ReadAsString(); + break; + + case nameof(SMethod.displayName): + displayName = reader.ReadAsString(); + break; + + case nameof(SMethod.metadataToken): + metadataToken = reader.ReadAsInt32() ?? default(int); + break; + + case nameof(SMethod.simpleName): + simpleName = reader.ReadAsString(); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new SMethod( + assemblyName ?? string.Empty, + displayName ?? string.Empty, + metadataToken, + simpleName ?? string.Empty + ); + } + + private SType ReadSType(JsonReader reader) { + string assemblyName = null; + string typeName = null; + int? metadataToken = null; + + while (reader.Read()) { + if (reader.TokenType == JsonToken.Null) { + return null; + } + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(SType.assemblyName): + assemblyName = reader.ReadAsString(); + break; + + case nameof(SType.typeName): + typeName = reader.ReadAsString(); + break; + + case nameof(SType.metadataToken): + metadataToken = reader.ReadAsInt32(); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new SType( + assemblyName ?? string.Empty, + typeName ?? string.Empty, + metadataToken ?? 0 + ); + } + + private SUnityJob ReadSUnityJob(JsonReader reader) { + int metadataToken = default(int); + UnityJobKind jobKind = default(UnityJobKind); + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(SUnityJob.metadataToken): + metadataToken = reader.ReadAsInt32() ?? 0; + break; + + case nameof(SUnityJob.jobKind): + var jobKindStr = reader.ReadAsString(); + Enum.TryParse(jobKindStr, out jobKind); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new SUnityJob(metadataToken, jobKind); + } + + private SField ReadSField(JsonReader reader) { + SType declaringType = null; + string fieldName = null; + string assemblyName = null; + int? metadataToken = null; + bool? serializable = null; + bool? isStatic = null; + + while (reader.Read()) { + if (reader.TokenType == JsonToken.EndObject) { + break; + } + if (reader.TokenType != JsonToken.PropertyName) { + continue; + } + var propertyName = (string)reader.Value; + + switch (propertyName) { + case nameof(SField.declaringType): + declaringType = ReadSType(reader); + break; + + case nameof(SField.fieldName): + fieldName = reader.ReadAsString(); + break; + + case nameof(SField.assemblyName): + assemblyName = reader.ReadAsString(); + break; + + case nameof(SField.metadataToken): + metadataToken = reader.ReadAsInt32(); + break; + + case nameof(SField.serializable): + serializable = reader.ReadAsBoolean(); + break; + + case nameof(SField.isStatic): + isStatic = reader.ReadAsBoolean(); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new SField(declaringType: declaringType, fieldName: fieldName, assemblyName: assemblyName, metadataToken ?? 0, isStatic ?? false, serializable ?? false); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { + var responses = (List)value; + if (responses == null) { + writer.WriteNull(); + return; + } + + writer.WriteStartArray(); + foreach (var response in responses) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(response.id)); + writer.WriteValue(response.id); + + if (response.patches != null) { + writer.WritePropertyName(nameof(response.patches)); + writer.WriteStartArray(); + foreach (var responsePatch in response.patches) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(responsePatch.patchId)); + writer.WriteValue(responsePatch.patchId); + writer.WritePropertyName(nameof(responsePatch.assemblyName)); + writer.WriteValue(responsePatch.assemblyName); + writer.WritePropertyName(nameof(responsePatch.patchAssembly)); + writer.WriteValue(Convert.ToBase64String(responsePatch.patchAssembly)); + writer.WritePropertyName(nameof(responsePatch.patchPdb)); + writer.WriteValue(Convert.ToBase64String(responsePatch.patchPdb)); + + if (responsePatch.modifiedMethods != null) { + writer.WritePropertyName(nameof(responsePatch.modifiedMethods)); + writer.WriteStartArray(); + foreach (var modifiedMethod in responsePatch.modifiedMethods) { + WriteSMethod(writer, modifiedMethod); + } + writer.WriteEndArray(); + } + + if (responsePatch.patchMethods != null) { + writer.WritePropertyName(nameof(responsePatch.patchMethods)); + writer.WriteStartArray(); + foreach (var patchMethod in responsePatch.patchMethods) { + WriteSMethod(writer, patchMethod); + } + writer.WriteEndArray(); + } + + if (responsePatch.newMethods != null) { + writer.WritePropertyName(nameof(responsePatch.newMethods)); + writer.WriteStartArray(); + foreach (var newMethod in responsePatch.newMethods) { + WriteSMethod(writer, newMethod); + } + writer.WriteEndArray(); + } + + if (responsePatch.unityJobs != null) { + writer.WritePropertyName(nameof(responsePatch.unityJobs)); + writer.WriteStartArray(); + foreach (var unityJob in responsePatch.unityJobs) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(unityJob.metadataToken)); + writer.WriteValue(unityJob.metadataToken); + writer.WritePropertyName(nameof(unityJob.jobKind)); + writer.WriteValue(unityJob.jobKind.ToString()); + + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + if (responsePatch.newFields != null) { + writer.WritePropertyName(nameof(responsePatch.newFields)); + writer.WriteStartArray(); + foreach (var newField in responsePatch.newFields) { + WriteSField(writer, newField); + } + writer.WriteEndArray(); + } + + if (responsePatch.deletedFields != null) { + writer.WritePropertyName(nameof(responsePatch.deletedFields)); + writer.WriteStartArray(); + foreach (var deletedField in responsePatch.deletedFields) { + WriteSField(writer, deletedField); + } + writer.WriteEndArray(); + } + + if (responsePatch.renamedFieldsFrom != null) { + writer.WritePropertyName(nameof(responsePatch.renamedFieldsFrom)); + writer.WriteStartArray(); + foreach (var removedFieldFrom in responsePatch.renamedFieldsFrom) { + WriteSField(writer, removedFieldFrom); + } + writer.WriteEndArray(); + } + + if (responsePatch.renamedFieldsTo != null) { + writer.WritePropertyName(nameof(responsePatch.renamedFieldsTo)); + writer.WriteStartArray(); + foreach (var removedFieldTo in responsePatch.renamedFieldsTo) { + WriteSField(writer, removedFieldTo); + } + writer.WriteEndArray(); + } + + if (responsePatch.propertyAttributesFieldOriginal != null) { + writer.WritePropertyName(nameof(responsePatch.propertyAttributesFieldOriginal)); + writer.WriteStartArray(); + foreach (var removedFieldFrom in responsePatch.propertyAttributesFieldOriginal) { + WriteSField(writer, removedFieldFrom); + } + writer.WriteEndArray(); + } + + if (responsePatch.propertyAttributesFieldUpdated != null) { + writer.WritePropertyName(nameof(responsePatch.propertyAttributesFieldUpdated)); + writer.WriteStartArray(); + foreach (var removedFieldTo in responsePatch.propertyAttributesFieldUpdated) { + WriteSField(writer, removedFieldTo); + } + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + if (response.failures != null) { + writer.WritePropertyName(nameof(response.failures)); + writer.WriteStartArray(); + foreach (var failure in response.failures) { + writer.WriteValue(failure); + } + writer.WriteEndArray(); + } + + if (response.removedMethod != null) { + writer.WritePropertyName(nameof(response.removedMethod)); + writer.WriteStartArray(); + foreach (var removedMethod in response.removedMethod) { + WriteSMethod(writer, removedMethod); + } + writer.WriteEndArray(); + } + + if (response.alteredFields != null) { + writer.WritePropertyName(nameof(response.alteredFields)); + writer.WriteStartArray(); + foreach (var alteredField in response.alteredFields) { + WriteSField(writer, alteredField); + } + writer.WriteEndArray(); + } + + if (response.addedFieldInitializerFields != null) { + writer.WritePropertyName(nameof(response.addedFieldInitializerFields)); + writer.WriteStartArray(); + foreach (var addedFieldInitializerField in response.addedFieldInitializerFields) { + WriteSField(writer, addedFieldInitializerField); + } + writer.WriteEndArray(); + } + + if (response.addedFieldInitializerInitializers != null) { + writer.WritePropertyName(nameof(response.addedFieldInitializerInitializers)); + writer.WriteStartArray(); + foreach (var addedFieldInitializerInitializer in response.addedFieldInitializerInitializers) { + WriteSMethod(writer, addedFieldInitializerInitializer); + } + writer.WriteEndArray(); + } + + if (response.removedFieldInitializers != null) { + writer.WritePropertyName(nameof(response.removedFieldInitializers)); + writer.WriteStartArray(); + foreach (var removedFieldInitializer in response.removedFieldInitializers) { + WriteSField(writer, removedFieldInitializer); + } + writer.WriteEndArray(); + } + + if (response.newFieldDefinitions != null) { + writer.WritePropertyName(nameof(response.newFieldDefinitions)); + writer.WriteStartArray(); + foreach (var newFieldDefinition in response.newFieldDefinitions) { + WriteSField(writer, newFieldDefinition); + } + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + void WriteSMethod(JsonWriter writer, SMethod method) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(method.assemblyName)); + writer.WriteValue(method.assemblyName); + writer.WritePropertyName(nameof(method.displayName)); + writer.WriteValue(method.displayName); + writer.WritePropertyName(nameof(method.metadataToken)); + writer.WriteValue(method.metadataToken); + writer.WritePropertyName(nameof(method.simpleName)); + writer.WriteValue(method.simpleName); + + writer.WriteEndObject(); + } + + void WriteSField(JsonWriter writer, SField field) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(field.declaringType)); + writer.WriteSType(field.declaringType); + writer.WritePropertyName(nameof(field.fieldName)); + writer.WriteValue(field.fieldName); + writer.WritePropertyName(nameof(field.assemblyName)); + writer.WriteValue(field.assemblyName); + writer.WritePropertyName(nameof(field.metadataToken)); + writer.WriteValue(field.metadataToken); + writer.WritePropertyName(nameof(field.serializable)); + writer.WriteValue(field.serializable); + + writer.WriteEndObject(); + } + + } + internal static class MethodPatchResponsesConverterExtensions { + public static void WriteSType(this JsonWriter writer, SType type) { + if (type == null) { + writer.WriteNull(); + return; + } + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(type.assemblyName)); + writer.WriteValue(type.assemblyName); + writer.WritePropertyName(nameof(type.typeName)); + writer.WriteValue(type.typeName); + writer.WritePropertyName(nameof(type.metadataToken)); + writer.WriteValue(type.metadataToken); + + writer.WriteEndObject(); + } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs.meta new file mode 100644 index 0000000..8652c10 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a35195732a094716a76d7125122c90fe +timeCreated: 1685732397 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs new file mode 100644 index 0000000..6edc784 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs @@ -0,0 +1,33 @@ +using System; +using System.Reflection; + +namespace SingularityGroup.HotReload { + static class MethodUtils { +#if ENABLE_MONO + public static unsafe void DisableVisibilityChecks(MethodBase method) { + if(IntPtr.Size == sizeof(long)) { + var ptr = (Interop.MonoMethod64*)method.MethodHandle.Value.ToPointer(); + ptr->monoMethodFlags |= Interop.MonoMethodFlags.skip_visibility; + } else { + var ptr = (Interop.MonoMethod32*)method.MethodHandle.Value.ToPointer(); + ptr->monoMethodFlags |= Interop.MonoMethodFlags.skip_visibility; + } + } + + public static unsafe bool IsMethodInlined(MethodBase method) { + if(IntPtr.Size == sizeof(long)) { + var ptr = (Interop.MonoMethod64*)method.MethodHandle.Value.ToPointer(); + return (ptr -> monoMethodFlags & Interop.MonoMethodFlags.inline_info) == Interop.MonoMethodFlags.inline_info; + } else { + var ptr = (Interop.MonoMethod32*)method.MethodHandle.Value.ToPointer(); + return (ptr -> monoMethodFlags & Interop.MonoMethodFlags.inline_info) == Interop.MonoMethodFlags.inline_info; + } + } +#else + public static void DisableVisibilityChecks(MethodBase method) { } + public static bool IsMethodInlined(MethodBase method) { + return false; + } +#endif + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs.meta new file mode 100644 index 0000000..d8ffd8e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c4f19b17adc17a94192a325012f153db +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/Runtime/MethodUtils.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours.meta new file mode 100644 index 0000000..fa0e4ef --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a2fd781ae18045f0b7690cd490737996 +timeCreated: 1675064423 \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs new file mode 100644 index 0000000..5aa2995 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs @@ -0,0 +1,82 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using SingularityGroup.HotReload.Localization; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + internal class ConnectionDialog : MonoBehaviour { + + [Header(Localization.Translations.MenuItems.UIControls)] + public Button buttonHide; + + [Header(Localization.Translations.MenuItems.Information)] + public Text textSummary; + public Text textSuggestion; + + void Start() { + buttonHide.onClick.AddListener(Hide); + } + + public int pendingPatches = 0; + public int patchesApplied = 0; + + private void Awake() { + SyncPatchCounts(); + } + + bool SyncPatchCounts() { + var changed = false; + if (pendingPatches != CodePatcher.I.PendingPatches.Count) { + pendingPatches = CodePatcher.I.PendingPatches.Count; + changed = true; + } + + if (patchesApplied != CodePatcher.I.PatchesApplied) { + patchesApplied = CodePatcher.I.PatchesApplied; + changed = true; + } + + return changed; + } + + /// One of the constants + public void SetSummary(string summary) { + if (textSummary != null) textSummary.text = summary; + isConnected = summary == ConnectionSummary.Connected; + } + + private bool isConnected = false; + + // assumes that auto-pair already tried for several seconds + void Update() { + textSuggestion.enabled = isConnected; + if (SyncPatchCounts()) { + textSuggestion.text = string.Format(Localization.Translations.Dialogs.PatchesStatus, pendingPatches, patchesApplied); + } + } + + /// hide this dialog + void Hide() { + gameObject.SetActive(false); // this should disable the Update loop? + } + } + + /// + /// The connection between device and Hot Reload can be summarized in a few words. + /// + /// + /// The summary may be shown for less than a second, as the connection can change without warning.
+ /// Therefore, we use short and simple messages. + ///
+ internal static class ConnectionSummary { + public static string Cancelled => Localization.Translations.Dialogs.ConnectionStateCancelled; + public static string Connecting => Localization.Translations.Dialogs.ConnectionStateConnecting; + public static string Handshaking => Localization.Translations.Dialogs.ConnectionStateHandshaking; + public static string DifferencesFound => Localization.Translations.Dialogs.ConnectionStateDifferencesFound; + public static string Connected => Localization.Translations.Dialogs.ConnectionStateConnected; + // reconnecting can be shown for a long time, so a longer message is okay + public static string TryingToReconnect => Localization.Translations.Dialogs.TryingToReconnect; + public static string Disconnected => Localization.Translations.Dialogs.Disconnected; + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs.meta new file mode 100644 index 0000000..929d769 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bb1cc47c374f478e861f2c3dade07e1a +timeCreated: 1675064498 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs new file mode 100644 index 0000000..fe812d4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs @@ -0,0 +1,144 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Collections; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.EventSystems; +#if ENABLE_INPUT_SYSTEM +using UnityEngine.InputSystem; +#endif + +namespace SingularityGroup.HotReload { + internal class Prompts : MonoBehaviour { + public GameObject retryPrompt; + public GameObject connectedPrompt; + public GameObject questionPrompt; + + [Header(Localization.Translations.MenuItems.Other)] + [Tooltip(Localization.Translations.MenuItems.FalllbackEventSystem)] + public GameObject fallbackEventSystem; + + #region Singleton + + private static Prompts _I; + + /// + /// All usages must check that is true before accessing this singleton. + /// + /// + /// This getter can throw on unsupported platforms (HotReloadSettingsObject resource doesn't exist on unsupported platforms). + /// + public static Prompts I { + get { + if (_I == null) { + // allow showing prompts in editor (for testing) + if (!Application.isEditor && !PlayerEntrypoint.IsPlayerWithHotReload()) { + throw new NotSupportedException(Localization.Translations.Errors.IsPlayerWithHotReloadFalse); + } + var go = Instantiate(HotReloadSettingsObject.I.PromptsPrefab, + new Vector3(0, 0, 0), Quaternion.identity); + go.name = nameof(Prompts) + "_singleton"; + if (Application.isPlaying) { + DontDestroyOnLoad(go); + } + + _I = go.GetComponentInChildren(); + } + + return _I; + } + } + #endregion + + /// + public static void SetConnectionState(string state, bool log = true) { + var connectionDialog = I.connectedPrompt.GetComponentInChildren(); + if (log) Log.Debug($"SetConnectionState( {state} )"); + if (connectionDialog) { + connectionDialog.SetSummary(state); + } + } + + /// + public static void ShowConnectionDialog() { + I.retryPrompt.SetActive(false); + I.connectedPrompt.SetActive(true); + } + + public static async Task ShowQuestionDialog(QuestionDialog.Config config) { + var tcs = new TaskCompletionSource(); + var holder = I.questionPrompt; + var dialog = holder.GetComponentInChildren(); + dialog.completion = tcs; + dialog.UpdateView(config); + holder.SetActive(true); + return await tcs.Task; + } + + public static void ShowRetryDialog( + PatchServerInfo patchServerInfo, + ServerHandshake.Result handshakeResults = ServerHandshake.Result.None, + bool auto = true + ) { + + var retryDialog = I.retryPrompt.GetComponentInChildren(); + + RetryDialog.TargetServer = patchServerInfo; + RetryDialog.HandshakeResults = handshakeResults; + + if (patchServerInfo == null) { + retryDialog.DebugInfo = $"patchServerInfo == null {handshakeResults}"; + } else { + retryDialog.DebugInfo = $"{RequestHelper.CreateUrl(patchServerInfo)} {handshakeResults}"; + } + retryDialog.autoConnect = auto; + + I.connectedPrompt.SetActive(false); + I.retryPrompt.SetActive(true); + } + + #region fallback event system + + private void Start() { + StartCoroutine(DelayedEnsureEventSystem()); + } + + private bool userTriedToInteract = false; + + private void Update() { + if (!userTriedToInteract) { + // when user interacts with the screen, make sure overlay can handle taps +#if ENABLE_INPUT_SYSTEM + if ((Touchscreen.current != null && Touchscreen.current.touches.Count > 0) || + (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)) { + userTriedToInteract = true; + DoEnsureEventSystem(); + } +#else + if (Input.touchCount > 0 || Input.GetMouseButtonDown(0)) { + userTriedToInteract = true; + DoEnsureEventSystem(); + } +#endif + } + } + + private IEnumerator DelayedEnsureEventSystem() { + // allow some delay in-case the project loads the EventSystem asynchronously (perhaps in a second scene) + if (EventSystem.current == null) { + yield return new WaitForSeconds(1f); + DoEnsureEventSystem(); + } + } + + /// Scene must contain an EventSystem and StandaloneInputModule, otherwise clicking/tapping on the overlay does nothing. + private void DoEnsureEventSystem() { + if (EventSystem.current == null) { + Log.Info(string.Format(Localization.Translations.Settings.NoEventSystemWarning, name)); + fallbackEventSystem.SetActive(true); + } + } + #endregion + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs.meta new file mode 100644 index 0000000..ff01518 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d92cdbfacafd433ca77184c22a384a6d +timeCreated: 1674488132 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs new file mode 100644 index 0000000..d257912 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs @@ -0,0 +1,67 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Localization; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + class QuestionDialog : MonoBehaviour { + + [Header(Localization.Translations.MenuItems.Information)] + public Text textSummary; + public Text textSuggestion; + + [Header(Localization.Translations.MenuItems.UIControls)] + public Button buttonContinue; + public Button buttonCancel; + public Button buttonMoreInfo; + + public TaskCompletionSource completion = new TaskCompletionSource(); + + public void UpdateView(Config config) { + textSummary.text = config.summary; + textSuggestion.text = config.suggestion; + + if (string.IsNullOrEmpty(config.continueButtonText)) { + buttonContinue.enabled = false; + } else { + buttonContinue.GetComponentInChildren().text = config.continueButtonText; + buttonContinue.onClick.AddListener(() => { + completion.TrySetResult(true); + Hide(); + }); + } + + if (string.IsNullOrEmpty(config.cancelButtonText)) { + buttonCancel.enabled = false; + } else { + buttonCancel.GetComponentInChildren().text = config.cancelButtonText; + buttonCancel.onClick.AddListener(() => { + completion.TrySetResult(false); + Hide(); + }); + } + + buttonMoreInfo.onClick.AddListener(() => { + Application.OpenURL(config.moreInfoUrl); + }); + } + + internal class Config { + public string summary; + public string suggestion; + public string continueButtonText = Localization.Translations.Dialogs.ContinueButtonText; + public string cancelButtonText = Localization.Translations.Dialogs.CancelButtonText; + public string moreInfoUrl = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? + "https://hotreload.net/zh/documentation/on-device#处理不同的提交" : + "https://hotreload.net/documentation/on-device#handling-different-commits"; + } + + /// hide this dialog + void Hide() { + gameObject.SetActive(false); // this should disable the Update loop? + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs.meta new file mode 100644 index 0000000..4cb6859 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ef31038a0ed84685b779466bf22d53a9 +timeCreated: 1675143382 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs new file mode 100644 index 0000000..d9daaf4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs @@ -0,0 +1,108 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using JetBrains.Annotations; +using SingularityGroup.HotReload.Localization; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + internal class RetryDialog : MonoBehaviour { + [Header(Localization.Translations.MenuItems.UIControls)] + public Button buttonHide; + public Button buttonRetryAutoPair; + public Button buttonTroubleshoot; + + public Text textSummary; + public Text textSuggestion; + public InputField ipInput; + + [Tooltip("Hidden by default")] + public Text textForDebugging; + + [Header("For HotReload Devs")] + // In Unity Editor, click checkbox to see info helpful for debugging bugs + public bool enableDebugging; + + // [Header("Other")] + // [Tooltip("Used when your project does not create an EventSystem early enough")] + // public GameObject fallbackEventSystem; + + private static RetryDialog _I; + + public string DebugInfo { + set { + textForDebugging.text = value; + } + } + + public bool autoConnect { get; set; } + + void Start() { + buttonHide.onClick.AddListener(() => { + Hide(); + }); + + buttonRetryAutoPair.onClick.AddListener(() => { + Hide(); + int port; + var ipAndPort = ipInput.textComponent.text.Split(':'); + if (ipAndPort.Length != 2 || !int.TryParse(ipAndPort[1], out port)) { + port = PlayerEntrypoint.PlayerBuildInfo?.buildMachinePort ?? RequestHelper.defaultPort; + } + var ip = ipAndPort.Length > 0 ? ipAndPort[0] : string.Empty; + PlayerEntrypoint.TryConnectToIpAndPort(ip, port); + }); + + buttonTroubleshoot.onClick.AddListener(() => { + var docsUrl = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? + "https://hotreload.net/zh/documentation/on-device#连接问题" : + "https://hotreload.net/documentation/on-device#connection-issues" ; + Application.OpenURL(docsUrl); + }); + } + + [CanBeNull] + public static PatchServerInfo TargetServer { private get; set; } = null; + public static ServerHandshake.Result HandshakeResults { private get; set; } = ServerHandshake.Result.None; + + private void OnEnable() { + ipInput.text = $"{PlayerEntrypoint.PlayerBuildInfo?.buildMachineHostName}:{PlayerEntrypoint.PlayerBuildInfo?.buildMachinePort}"; + UpdateUI(); + } + + void Update() { + UpdateUI(); + } + + void UpdateUI() { + // assumes that auto-pair already tried for several seconds + // suggestions to help the user when auto-pair is failing + var networkText = Application.isMobilePlatform ? "WiFi" : "LAN/WiFi"; + var noWifiNetwork = string.Format(Localization.Translations.Dialogs.IsConnected, networkText); + var waitForCompiling = Localization.Translations.Dialogs.WaitForCompiling; + var targetNetworkIsReachable = string.Format(Localization.Translations.Dialogs.TargetNetworkIsReachable, networkText); + + if (Application.internetReachability != NetworkReachability.ReachableViaLocalAreaNetwork) { + textSuggestion.text = noWifiNetwork; + } else if (HandshakeResults.HasFlag(ServerHandshake.Result.WaitForCompiling)) { + // Note: Technically the player could do the waiting itself, and handshake again with the server + // only after compiling finishes... Telling the user to do that is easier to implement though. + textSuggestion.text = waitForCompiling; + } else { + textSuggestion.text = targetNetworkIsReachable; + } + + textSummary.text = autoConnect ? Localization.Translations.Dialogs.AutoPairEncounteredIssue : Localization.Translations.Dialogs.ConnectionFailed; + + if (enableDebugging && textForDebugging) { + textForDebugging.enabled = true; + textForDebugging.text = $"the target = {TargetServer}"; + } + } + + /// hide this dialog + void Hide() { + gameObject.SetActive(false); // this should disable the Update loop? + } + } +} +#endif diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs.meta new file mode 100644 index 0000000..8a3dc77 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7a69f8e8e50a405a84ec22ac7c2f4bdc +timeCreated: 1674408078 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs new file mode 100644 index 0000000..2a30bab --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs @@ -0,0 +1,194 @@ +using System; +using System.Runtime.InteropServices; + + +namespace SingularityGroup.HotReload.Interop { + //see _MonoMethod struct in class-internals.h + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto, Size = 8 + sizeof(long) * 3 + 4)] + internal unsafe struct MonoMethod64 { + [FieldOffset(0)] + public MethodAttributes flags; + [FieldOffset(2)] + public MethodImplAttributes iflags; + [FieldOffset(4)] + public uint token; + [FieldOffset(8)] + public void* klass; + [FieldOffset(8 + sizeof(long))] + public void* signature; + [FieldOffset(8 + sizeof(long) * 2)] + public char* name; + /* this is used by the inlining algorithm */ + [FieldOffset(8 + sizeof(long) * 3)] + public MonoMethodFlags monoMethodFlags; + [FieldOffset(8 + sizeof(long) * 3 + 2)] + public short slot; + } + + [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Auto, Size = 8 + sizeof(int) * 3 + 4)] + internal unsafe struct MonoMethod32 { + [FieldOffset(0)] + public MethodAttributes flags; + [FieldOffset(2)] + public MethodImplAttributes iflags; + [FieldOffset(4)] + public uint token; + [FieldOffset(8)] + public void* klass; + [FieldOffset(8 + sizeof(int))] + public void* signature; + [FieldOffset(8 + sizeof(int) * 2)] + public char* name; + /* this is used by the inlining algorithm */ + [FieldOffset(8 + sizeof(int) * 3)] + public MonoMethodFlags monoMethodFlags; + [FieldOffset(8 + sizeof(int) * 3 + 2)] + public short slot; + } + + //Corresponds to the bitflags of the _MonoMethod struct + [Flags] + internal enum MonoMethodFlags : ushort { + inline_info = 1 << 0, //:1 + inline_failure = 1 << 1, //:1 + wrapper_type = 1 << 2, //:5 + string_ctor = 1 << 7, //:1 + save_lmf = 1 << 8, //:1 + dynamic = 1 << 9, //:1 /* created & destroyed during runtime */ + sre_method = 1 << 10, //:1 /* created at runtime using Reflection.Emit */ + is_generic = 1 << 11, //:1 /* whenever this is a generic method definition */ + is_inflated = 1 << 12, //:1 /* whether we're a MonoMethodInflated */ + skip_visibility = 1 << 13, //:1 /* whenever to skip JIT visibility checks */ + verification_success = 1 << 14, //:1 /* whether this method has been verified successfully.*/ + } + + + [Flags] + internal enum MethodImplAttributes : ushort { + /// Specifies that the method implementation is in Microsoft intermediate language (MSIL). + IL = 0, + + /// Specifies that the method is implemented in managed code. + Managed = 0, + + /// Specifies that the method implementation is native. + Native = 1, + + /// Specifies that the method implementation is in Optimized Intermediate Language (OPTIL). + OPTIL = 2, + + /// Specifies flags about code type. + CodeTypeMask = 3, + + /// Specifies that the method implementation is provided by the runtime. + Runtime = 3, + + /// Specifies whether the method is implemented in managed or unmanaged code. + ManagedMask = 4, + + /// Specifies that the method is implemented in unmanaged code. + Unmanaged = 4, + + /// Specifies that the method cannot be inlined. + NoInlining = 8, + + /// Specifies that the method is not defined. + ForwardRef = 16, // 0x00000010 + + /// Specifies that the method is single-threaded through the body. Static methods (Shared in Visual Basic) lock on the type, whereas instance methods lock on the instance. You can also use the C# lock statement or the Visual Basic SyncLock statement for this purpose. + Synchronized = 32, // 0x00000020 + + /// Specifies that the method is not optimized by the just-in-time (JIT) compiler or by native code generation (see Ngen.exe) when debugging possible code generation problems. + NoOptimization = 64, // 0x00000040 + + /// Specifies that the method signature is exported exactly as declared. + PreserveSig = 128, // 0x00000080 + + /// Specifies that the method should be inlined wherever possible. + AggressiveInlining = 256, // 0x00000100 + + /// Specifies an internal call. + InternalCall = 4096, // 0x00001000 + + /// Specifies a range check value. + MaxMethodImplVal = 65535, // 0x0000FFFF + } + + + + /// Specifies flags for method attributes. These flags are defined in the corhdr.h file. + [Flags] + internal enum MethodAttributes : ushort { + /// Retrieves accessibility information. + MemberAccessMask = 7, + + /// Indicates that the member cannot be referenced. + PrivateScope = 0, + + /// Indicates that the method is accessible only to the current class. + Private = 1, + + /// Indicates that the method is accessible to members of this type and its derived types that are in this assembly only. + FamANDAssem = 2, + + /// Indicates that the method is accessible to any class of this assembly. + Assembly = FamANDAssem | Private, // 0x00000003 + + /// Indicates that the method is accessible only to members of this class and its derived classes. + Family = 4, + + /// Indicates that the method is accessible to derived classes anywhere, as well as to any class in the assembly. + FamORAssem = Family | Private, // 0x00000005 + + /// Indicates that the method is accessible to any object for which this object is in scope. + Public = Family | FamANDAssem, // 0x00000006 + + /// Indicates that the method is defined on the type; otherwise, it is defined per instance. + Static = 16, // 0x00000010 + + /// Indicates that the method cannot be overridden. + Final = 32, // 0x00000020 + + /// Indicates that the method is virtual. + Virtual = 64, // 0x00000040 + + /// Indicates that the method hides by name and signature; otherwise, by name only. + HideBySig = 128, // 0x00000080 + + /// Indicates that the method can only be overridden when it is also accessible. + CheckAccessOnOverride = 512, // 0x00000200 + + /// Retrieves vtable attributes. + VtableLayoutMask = 256, // 0x00000100 + + /// Indicates that the method will reuse an existing slot in the vtable. This is the default behavior. + ReuseSlot = 0, + + /// Indicates that the method always gets a new slot in the vtable. + NewSlot = VtableLayoutMask, // 0x00000100 + + /// Indicates that the class does not provide an implementation of this method. + Abstract = 1024, // 0x00000400 + + /// Indicates that the method is special. The name describes how this method is special. + SpecialName = 2048, // 0x00000800 + + /// Indicates that the method implementation is forwarded through PInvoke (Platform Invocation Services). + PinvokeImpl = 8192, // 0x00002000 + + /// Indicates that the managed method is exported by thunk to unmanaged code. + UnmanagedExport = 8, + + /// Indicates that the common language runtime checks the name encoding. + RTSpecialName = 4096, // 0x00001000 + + /// Indicates a reserved flag for runtime use only. + ReservedMask = 53248, // 0x0000D000 + + /// Indicates that the method has security associated with it. Reserved flag for runtime use only. + HasSecurity = 16384, // 0x00004000 + + /// Indicates that the method calls another method containing security code. Reserved flag for runtime use only. + RequireSecObject = 32768, // 0x00008000 + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs.meta new file mode 100644 index 0000000..6950a56 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bb9456a28c3b8644e9b0f78eb6d9ac17 +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/Runtime/MonoMethod.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs new file mode 100644 index 0000000..c115acf --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace SingularityGroup.HotReload { + // Largely copied from CommandLineParameters.ReadIsClone of UnityEditor.MultiplayerModule.dll + internal static class MultiplayerPlaymodeHelper { +#if UNITY_EDITOR && UNITY_2023_1_OR_NEWER + public static bool? isClone; + public static bool IsClone => isClone == null ? (isClone = HasCommandLineArgument(Environment.GetCommandLineArgs(), "--virtual-project-clone")).Value : isClone.Value; +#else + public static bool IsClone => false; +#endif + + public static bool HasCommandLineArgument(string[] commandLineArgs, string argumentName) { + foreach (string commandLineArg in commandLineArgs) { + if (commandLineArg == argumentName) { + return true; + } + } + return false; + } + + public static string PathToMainProject(string path) { + if (IsClone) { + // Library/VP/ is the base path + return Path.Combine("..", "..", "..", path); + } + return path; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs.meta new file mode 100644 index 0000000..41fcda0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: caf15b822bf74e8491daf33e58c4f11e +timeCreated: 1768677740 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MultiplayerPlaymodeHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OSX.meta b/Packages/com.singularitygroup.hotreload/Runtime/OSX.meta new file mode 100644 index 0000000..8a2fdb5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/OSX.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 62fd71232a4784cdeb53a6ab67694087 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib b/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib new file mode 100644 index 0000000..b63141a Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib differ diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib.meta b/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib.meta new file mode 100644 index 0000000..e1531b9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib.meta @@ -0,0 +1,87 @@ +fileFormatVersion: 2 +guid: 56a90f73ab41d4145bb173186644f641 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs new file mode 100644 index 0000000..bc85a30 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs @@ -0,0 +1,193 @@ +#pragma warning disable CS0618 // obsolete warnings (stay warning-free also in newer unity versions) +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; +using Object = UnityEngine.Object; +using SingularityGroup.HotReload.Localization; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace SingularityGroup.HotReload { + + static class Dispatch { + // DispatchOnHotReload is called every time a patch is applied (1x per batch of filechanges) + public static async Task OnHotReload(List patchedMethods) { + var methods = await Task.Run(() => GetOrFillMethodsCacheThreaded()); + + foreach (var m in methods) { + if (m.IsStatic) { + InvokeStaticMethod(m, nameof(InvokeOnHotReload), patchedMethods); + } else { + foreach (var go in GameObject.FindObjectsOfType(m.DeclaringType)) { + InvokeInstanceMethod(m, go, patchedMethods); + } + } + } + } + + public static void OnHotReloadLocal(MethodBase originalMethod, MethodBase patchMethod) { + if (!Attribute.IsDefined(originalMethod, typeof(InvokeOnHotReloadLocal))) { + return; + } + var attrib = Attribute.GetCustomAttribute(originalMethod, typeof(InvokeOnHotReloadLocal)) as InvokeOnHotReloadLocal; + + if (!string.IsNullOrEmpty(attrib?.methodToInvoke)) { + OnHotReloadLocalCustom(originalMethod, attrib); + return; + } + var patchMethodParams = patchMethod.GetParameters(); + if (patchMethodParams.Length == 0) { + InvokeStaticMethod(patchMethod, nameof(InvokeOnHotReloadLocal), null); + } else if (typeof(MonoBehaviour).IsAssignableFrom(patchMethodParams[0].ParameterType)) { + foreach (var go in GameObject.FindObjectsOfType(patchMethodParams[0].ParameterType)) { + InvokeInstanceMethodStatic(patchMethod, go); + } + } else { + Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {patchMethod.DeclaringType?.Name} {patchMethod.Name} {Localization.Translations.Utility.MethodCallWarning}"); + } + } + + public static void OnHotReloadLocalCustom(MethodBase origianlMethod, InvokeOnHotReloadLocal attrib) { + var reloadForType = origianlMethod.DeclaringType; + var reloadMethod = reloadForType?.GetMethod(attrib.methodToInvoke, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + if (reloadMethod == null) { + Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {string.Format(Localization.Translations.Utility.OnHotReloadLocalWarning, attrib.methodToInvoke)}"); + return; + } + if (reloadMethod.IsStatic) { + InvokeStaticMethod(reloadMethod, nameof(InvokeOnHotReloadLocal), null); + } else if (typeof(MonoBehaviour).IsAssignableFrom(reloadForType)) { + foreach (var go in GameObject.FindObjectsOfType(reloadForType)) { + InvokeInstanceMethod(reloadMethod, go, null); + } + } else { + Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {reloadMethod.DeclaringType?.Name} {reloadMethod.Name} {Localization.Translations.Utility.MethodCallWarning}"); + } + } + + private static List methodsCache; + + private static List GetOrFillMethodsCacheThreaded() { + if (methodsCache != null) { + return methodsCache; + } + +#if UNITY_2019_1_OR_NEWER && UNITY_EDITOR + var methodCollection = UnityEditor.TypeCache.GetMethodsWithAttribute(typeof(InvokeOnHotReload)); + var methods = new List(); + foreach (var m in methodCollection) { + methods.Add(m); + } +#else + var methods = GetMethodsReflection(); +#endif + + methodsCache = methods; + return methods; + } + + private static List GetMethodsReflection() { + var methods = new List(); + + try { + foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { + if (asm.FullName == "System" || asm.FullName.StartsWith("System.", StringComparison.Ordinal)) { + continue; // big performance optimization + } + + try { + foreach (var type in asm.GetTypes()) { + try { + foreach (var m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { + try { + if (Attribute.IsDefined(m, typeof(InvokeOnHotReload))) { + methods.Add(m); + } + } catch (BadImageFormatException) { + // silently ignore (can happen, is very annoying if it spams) + /* + BadImageFormatException: VAR 3 (TOutput) cannot be expanded in this context with 3 instantiations + System.Reflection.MonoMethod.GetBaseMethod () (at :0) + System.MonoCustomAttrs.GetBase (System.Reflection.ICustomAttributeProvider obj) (at :0) + System.MonoCustomAttrs.IsDefined (System.Reflection.ICustomAttributeProvider obj, System.Type attributeType, System.Boolean inherit) (at :0) + */ + } catch (TypeLoadException) { + // silently ignore (can happen, is very annoying if it spams) + } catch (Exception e) { + ThreadUtility.LogException(new AggregateException(type.Name + "." + m.Name, e)); + } + } + } catch (BadImageFormatException) { + // silently ignore (can happen, is very annoying if it spams) + } catch (TypeLoadException) { + // silently ignore (can happen, is very annoying if it spams) + } catch (Exception e) { + ThreadUtility.LogException(new AggregateException(type.Name, e)); + } + } + } catch (BadImageFormatException) { + // silently ignore (can happen, is very annoying if it spams) + } catch (TypeLoadException) { + // silently ignore (can happen, is very annoying if it spams) + } catch (Exception e) { + ThreadUtility.LogException(new AggregateException(asm.FullName, e)); + } + } + } catch (Exception e) { + ThreadUtility.LogException(e); + } + return methods; + } + + private static void InvokeStaticMethod(MethodBase m, string attrName, List patchedMethods) { + try { + if (patchedMethods != null && m.GetParameters().Length == 1) { + m.Invoke(null, new object[] { patchedMethods }); + } else { + m.Invoke(null, new object[] { }); + } + } catch (Exception e) { + if (m.GetParameters().Length != 0) { + Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} {Localization.Translations.Utility.OnHotReloadWarning}\n{e}"); + } else { + Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} failed. Exception\n{e}"); + } + } + } + + private static void InvokeInstanceMethod(MethodBase m, Object go, List patchedMethods) { + try { + if (patchedMethods != null && m.GetParameters().Length == 1) { + m.Invoke(go, new object[] { patchedMethods }); + } else { + m.Invoke(go, new object[] { }); + } + } catch (Exception e) { + if (m.GetParameters().Length != 0) { + Log.Warning($"[InvokeOnHotReload] {m.DeclaringType?.Name} {m.Name} {Localization.Translations.Utility.OnHotReloadWarning}\n{e}"); + } else { + Log.Warning(string.Format(Localization.Translations.Logging.InvokeOnHotReloadFailed, m.DeclaringType?.Name, m.Name, e)); + } + } + } + + private static void InvokeInstanceMethodStatic(MethodBase m, Object go) { + try { + m.Invoke(null, new object[] { go }); + } catch (Exception e) { + if (m.GetParameters().Length != 0) { + Log.Warning($"[InvokeOnHotReloadLocal] {m.DeclaringType?.Name} {m.Name} {Localization.Translations.Utility.OnHotReloadLocalCallWarning}\n{e}"); + } else { + Log.Warning(Localization.Translations.Logging.InvokeOnHotReloadLocalFailed, m.DeclaringType?.Name, m.Name, e); + } + } + } + + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs.meta new file mode 100644 index 0000000..58aed81 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 8fde7dfa45d340eda4c6c64ccf52e17d +timeCreated: 1673824017 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs new file mode 100644 index 0000000..3d8c50e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs @@ -0,0 +1,25 @@ +using System.IO; +using UnityEngine; + +namespace SingularityGroup.HotReload { + internal static class PackageConst { + //CI changes this property to 'true' for asset store builds. + //Don't touch unless you know what you are doing + public static bool IsAssetStoreBuild => true; + + + public const string Version = "1.13.17"; + // Never higher than Version + // Used for the download + public const string ServerVersion = "1.13.17"; + public const string PackageName = "com.singularitygroup.hotreload"; + public const string DefaultLocale = Localization.Locale.English; + // avoids unreachable code warnings from using const + public static string DefaultLocaleField = DefaultLocale; + public static readonly string LibraryCachePath = MultiplayerPlaymodeHelper.PathToMainProject("Library/" + PackageName); + public const string ConfigFileName = "hot-reload-config.json"; + public static readonly string ConfigFilePath = Path.Combine(MultiplayerPlaymodeHelper.PathToMainProject(ConfigFileName)); + public const string ServerInfoFileName = "serverinfo.json"; + public static readonly string ServerInfoFilePath = Path.Combine(LibraryCachePath, ServerInfoFileName); + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs.meta new file mode 100644 index 0000000..bd599e0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 877e785c22c57be45a4d79c1be346c79 +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/Runtime/PackageConst.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs new file mode 100644 index 0000000..ee45679 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs @@ -0,0 +1,39 @@ +using System; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEngine; + +namespace SingularityGroup.HotReload { + [Serializable] + class PatchServerInfo { + public readonly string hostName; + public readonly int port; + public readonly string commitHash; + public readonly string rootPath; + [Obsolete]public readonly bool isRemote; + public readonly string customRequestOrigin; + + public const string UnknownCommitHash = "unknown"; + + /// an ip address or "localhost" + public PatchServerInfo(string hostName, string commitHash, string rootPath) { + this.hostName = hostName; + this.commitHash = commitHash ?? UnknownCommitHash; + this.rootPath = rootPath; + this.port = RequestHelper.defaultPort; + } + + /// an ip address or "localhost" + // constructor should (must?) have a param for each field + [JsonConstructor] + public PatchServerInfo(string hostName, int port, string commitHash, string rootPath, bool isRemote = false, string customRequestOrigin = null) { + this.hostName = hostName; + this.port = port; + this.commitHash = commitHash ?? UnknownCommitHash; + this.rootPath = rootPath; +#pragma warning disable CS0612 // Type or member is obsolete + this.isRemote = isRemote; +#pragma warning restore CS0612 // Type or member is obsolete + this.customRequestOrigin = customRequestOrigin; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs.meta new file mode 100644 index 0000000..bfb9635 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5795d77991613ae48870b7c349491adc +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/Runtime/PatchServerInfo.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs new file mode 100644 index 0000000..3115f9c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace SingularityGroup.HotReload { + static class PersistencePaths { + public static string GetPatchesFilePath(string basePath) { + return Path.Combine(basePath, "CodePatcher", "patches.bin"); + } + + public static string GetServerInfoFilePath(string basePath) { + return Path.Combine(basePath, "CodePatcher", "hostInfo.json"); + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs.meta new file mode 100644 index 0000000..ab8386c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3742023437057a049842c5fad1019132 +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/Runtime/PersistencePaths.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs new file mode 100644 index 0000000..239d64b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + + static class PlayerCodePatcher { + static Timer timer; + + static PlayerCodePatcher() { + if (PlayerEntrypoint.IsPlayerWithHotReload()) { + timer = new Timer(OnIntervalThreaded, (Action) OnIntervalMainThread, 500, 500); + serverHealthyAt = DateTime.MinValue; + } + } + + private static DateTime serverHealthyAt; + private static TimeSpan TimeSinceServerHealthy() => DateTime.UtcNow - serverHealthyAt; + + /// + /// Set server that you want to try connect to. + /// + /// + /// + /// This allows repetitions of: + /// - try handshake + /// - success -> try healthcheck + /// - success -> poll method patches + /// - + /// + /// + /// Only do this after confirming (with /handshake) that server is compatible with this build.
+ /// The user will be prompted if handshake needs confirmation. + ///
+ ///
+ internal static Task UpdateHost(PatchServerInfo serverInfo) { + Log.Debug($"UpdateHost to {(serverInfo == null ? "null" : serverInfo.hostName)}"); + // In player builds, server is remote, se we don't load assemblies from any paths + RequestHelper.ChangeAssemblySearchPaths(Array.Empty()); + ServerHealthCheck.I.SetServerInfo(null); // stop doing health check on old server + RequestHelper.SetServerInfo(serverInfo); + // Show feedback about connection progress (handshake can take ~5 seconds for our big game) + if (serverInfo == null) { + Prompts.SetConnectionState(ConnectionSummary.Disconnected); + } else { + Prompts.SetConnectionState(ConnectionSummary.Connected); + Prompts.ShowConnectionDialog(); + } + return ServerHandshake.I.SetServerInfo(serverInfo); + } + + public static Task Disconnect() => UpdateHost(null); + + static void OnIntervalThreaded(object o) { + ServerHandshake.I.CheckHandshake(); + ServerHealthCheck.I.CheckHealthAsync().Forget(); + + ThreadUtility.RunOnMainThread((Action)o); + } + + static string lastPatchId = string.Empty; + static void OnIntervalMainThread() { + PatchServerInfo verifiedServer; + if(ServerHandshake.I.TryGetVerifiedServer(out verifiedServer)) { + // now that handshake verified, we are connected. + // Note: If there is delay between handshake done and chosing to connect, then it may be outdated. + Prompts.SetConnectionState(ConnectionSummary.Connecting); + // Note: verified does not imply that server is running, sometimes we verify the host just from the deeplink data + ServerHealthCheck.I.SetServerInfo(verifiedServer); + } + + if(ServerHealthCheck.I.IsServerHealthy) { + // we may have reconnected to the same host, after losing connection for several seconds + Prompts.SetConnectionState(ConnectionSummary.Connected, false); + serverHealthyAt = DateTime.UtcNow; + RequestHelper.PollMethodPatches(lastPatchId, resp => HandleResponseReceived(resp)); + } else if (ServerHealthCheck.I.WasServerResponding) { // only update prompt state if disconnected server + var secondsSinceHealthy = TimeSinceServerHealthy().TotalSeconds; + var reconnectTimeout = 30; // seconds + if (secondsSinceHealthy > 2) { + Log.Info(Localization.Translations.Common.HotReloadUnreachable); + // feedback for the user so they know why patches are not applying + Prompts.SetConnectionState($"{ConnectionSummary.TryingToReconnect} {reconnectTimeout - secondsSinceHealthy:F0}s", false); + Prompts.ShowConnectionDialog(); + } + if (secondsSinceHealthy > reconnectTimeout) { + // give up on the server, give user a way to connect to another + Log.Info(string.Format(Localization.Translations.Logging.HotReloadUnreachableDisconnecting, reconnectTimeout)); + var disconnectedServer = RequestHelper.ServerInfo; + Disconnect().Forget(); + // Let user tap button to retry connecting to the same server (maybe just need to run Hot Reload again) + // Assumption: prompt also has a way to connect to a different server + Prompts.ShowRetryDialog(disconnectedServer); + } + } + } + + static void HandleResponseReceived(MethodPatchResponse response) { + Log.Debug("PollMethodPatches handling MethodPatchResponse id:{0} response.patches.Length:{1} response.failures.Length:{2}", + response.id, response.patches.Length, response.failures.Length); + if(response.patches.Length > 0) { + CodePatcher.I.RegisterPatches(response, persist: true); + } + if(response.failures.Length > 0) { + foreach (var failure in response.failures) { + // feedback to user so they know why their patch wasn't applied + Log.Warning(failure); + } + } + lastPatchId = response.id; + } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs.meta new file mode 100644 index 0000000..7be7b5a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 41b853a967284f44b36c071abf30124c +timeCreated: 1674453262 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs new file mode 100644 index 0000000..1a0be6a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs @@ -0,0 +1,161 @@ +#if UNITY_ANDROID && !UNITY_EDITOR +#define MOBILE_ANDROID +#endif +#if UNITY_IOS && !UNITY_EDITOR +#define MOBILE_IOS +#endif +#if MOBILE_ANDROID || MOBILE_IOS +#define MOBILE +#endif + +using System; +using System.Threading.Tasks; +#if MOBILE_ANDROID +// not able to use File apis for reading from StreamingAssets +using UnityEngine.Networking; +#endif +using UnityEngine; +using Debug = UnityEngine.Debug; +using System.IO; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + // entrypoint for Unity Player builds. Not necessary in Unity Editor. + internal static class PlayerEntrypoint { + /// Set when behaviour is created, when you access this instance through the singleton, + /// you can assume that this field is not null. + /// + /// In Player code you can assume this is set.
+ /// When in Editor this is usually null. + ///
+ static BuildInfo buildInfo { get; set; } + + /// In Player code you can assume this is set (not null) + public static BuildInfo PlayerBuildInfo => buildInfo; + + #if ENABLE_MONO + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + #endif + private static void InitOnAppLoad() { + AppCallbackListener.Init(); // any platform might be using this + UnityHelper.Init(); + Translations.LoadDefaultLocalization(); + bool onlyPrefabMissing; + if (!IsPlayerWithHotReload(out onlyPrefabMissing)) { + if (onlyPrefabMissing) { + Log.Warning(Localization.Translations.Logging.HotReloadNotAvailableBuildSettings); + } + return; + } + + TryAutoConnect().Forget(); + } + + static async Task TryAutoConnect() { + try { + buildInfo = await GetBuildInfo(); + } catch (Exception e) { + if (e is IOException) { + Log.Warning(Localization.Translations.Logging.HotReloadNotAvailableBuildSettings); + } else { + Log.Error($"{Localization.Translations.Errors.UnknownExceptionReadingBuildInfo}\n{e.GetType().Name}: {e.Message}"); + } + return; + } + if (buildInfo == null) { + Log.Error(Localization.Translations.Errors.BuildInfoNotFound); + return; + } + + CodePatcher.I.debuggerCompatibilityEnabled = true; + + try { + var customIp = PlayerPrefs.GetString("HotReloadRuntime.CustomIP", ""); + if (!string.IsNullOrEmpty(customIp)) { + buildInfo.buildMachineHostName = customIp; + } + var customPort = PlayerPrefs.GetString("HotReloadRuntime.CustomPort", ""); + if (!string.IsNullOrEmpty(customPort)) { + buildInfo.buildMachinePort = int.Parse(customPort); + } + + if (buildInfo.BuildMachineServer == null) { + Prompts.ShowRetryDialog(null); + } else { + // try reach server running on the build machine. + TryConnect(buildInfo.BuildMachineServer, auto: true).Forget(); + } + } catch (Exception ex) { + Log.Exception(ex); + } + } + + public static Task TryConnectToIpAndPort(string ip, int port) { + ip = ip.Trim(); + if (buildInfo == null) { + throw new ArgumentException(Localization.Translations.Logging.BuildInfoNotFound); + } + buildInfo.buildMachineHostName = ip; + buildInfo.buildMachinePort = port; + PlayerPrefs.SetString("HotReloadRuntime.CustomIP", ip); + PlayerPrefs.SetString("HotReloadRuntime.CustomPort", port.ToString()); + return TryConnect(buildInfo.BuildMachineServer, auto: false); + } + + public static async Task TryConnect(PatchServerInfo serverInfo, bool auto) { + // try reach server running on the build machine. + var handshake = PlayerCodePatcher.UpdateHost(serverInfo); + await Task.WhenAny(handshake, Task.Delay(TimeSpan.FromSeconds(40))); + await ThreadUtility.SwitchToMainThread(); + var handshakeResults = await handshake; + var handshakeOk = handshakeResults.HasFlag(ServerHandshake.Result.Verified); + if (!handshakeOk) { + Log.Debug("ShowRetryPrompt because handshake result is {0}", handshakeResults); + Prompts.ShowRetryDialog(serverInfo, handshakeResults, auto); + // cancel trying to connect. They can use the retry button + PlayerCodePatcher.UpdateHost(null).Forget(); + } + + Log.Info(string.Format(Localization.Translations.Logging.ServerHealthyAfterHandshake, handshakeOk)); + } + + /// on Android, streaming assets are inside apk zip, which can only be read using unity web request + private static async Task GetBuildInfo() { + var path = BuildInfo.GetStoredPath(); + #if MOBILE_ANDROID + var json = await RequestHelper.GetAsync(path); + return await Task.Run(() => BuildInfo.FromJson(json)); + #else + return await Task.Run(() => { + return BuildInfo.FromJson(File.ReadAllText(path)); + }); + #endif + } + + public static bool IsPlayer() => !Application.isEditor; + + public static bool IsPlayerWithHotReload() { + bool _; + return IsPlayerWithHotReload(out _); + } + + public static bool IsPlayerWithHotReload(out bool onlyPrefabMissing) { + onlyPrefabMissing = false; + if (!IsPlayer() || !RuntimeSupportsHotReload || !HotReloadSettingsObject.I.IncludeInBuild) { + return false; + } + onlyPrefabMissing = !HotReloadSettingsObject.I.PromptsPrefab; + return !onlyPrefabMissing; + } + + public static bool RuntimeSupportsHotReload { + get { + #if DEVELOPMENT_BUILD && ENABLE_MONO + return true; + #else + return false; + #endif + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs.meta new file mode 100644 index 0000000..b1bdc5b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 79392e60063749df984beec2908685bf +timeCreated: 1674215564 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public.meta new file mode 100644 index 0000000..d48c86f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: da163599826f56e4f929d76e8e35c7e2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs b/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs new file mode 100644 index 0000000..009fdd5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs @@ -0,0 +1,17 @@ +namespace SingularityGroup.HotReload { + /// + /// Utility class to set the log level of the Hot Reload package + /// + public static class HotReloadLogging { + /// + /// Sets the log level for logs inside the Hot Reload package + /// The default log level is + /// + /// + /// To see more detailed logs, set the log level to + /// + public static void SetLogLevel(LogLevel level) { + Log.minLevel = level; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs.meta new file mode 100644 index 0000000..b521a67 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 48845e010a33e2d4993888d9c18e8d95 +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/Runtime/Public/HotReloadLogging.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs new file mode 100644 index 0000000..a814be8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs @@ -0,0 +1,32 @@ +using System; +using System.Reflection; + +namespace SingularityGroup.HotReload { + /// + /// Methods with this attribute will get invoked after a hot reload + /// + /// + /// The method with this attribute needs to have no parameters. + /// Further more it needs to either be static or and instance method inside a . + /// For the latter case the method of all instances of the will be called. + /// In case the method has a return value it will be ignored. + /// + [AttributeUsage(AttributeTargets.Method)] + public class InvokeOnHotReload : Attribute { + } + + public class MethodPatch { + // Compiled by the Unity Editor. Null for newly added methods. + public MethodBase originalMethod; + // Method before Hot Reload applied it's patch. Null for newly added methods. + public MethodBase previousMethod; + // Method generated by Hot Reload. + public MethodBase newMethod; + + public MethodPatch(MethodBase originalMethod, MethodBase previousMethod, MethodBase newMethod) { + this.originalMethod = originalMethod; + this.previousMethod = previousMethod; + this.newMethod = newMethod; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs.meta new file mode 100644 index 0000000..dd7c76e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: de8fb6e6e05ccae46a410932e1545848 +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/Runtime/Public/InvokeOnHotReload.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs new file mode 100644 index 0000000..4fa5ecf --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs @@ -0,0 +1,22 @@ +using System; + +namespace SingularityGroup.HotReload { + /// + /// Method with this attribute will get invoked when it gets patched + /// + /// + /// The method with this attribute needs to have no parameters. + /// Furthermore it needs to either be static or an instance method inside a . + /// For the latter case the method of all instances of the will be called. + /// In case the method has a return value it will be ignored. + /// + [AttributeUsage(AttributeTargets.Method)] + public class InvokeOnHotReloadLocal : Attribute { + public readonly string methodToInvoke; + + public InvokeOnHotReloadLocal(string methodToInvoke = null) { + this.methodToInvoke = methodToInvoke; + } + } + +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs.meta new file mode 100644 index 0000000..63f97f7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f4629f348dad47d4add28eca71be748c +timeCreated: 1694274524 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs b/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs new file mode 100644 index 0000000..d790311 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs @@ -0,0 +1,108 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; +#if (UNITY_2019_4_OR_NEWER) +using UnityEngine; +#endif + +namespace SingularityGroup.HotReload { + public static class Log { + [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")] + public static LogLevel minLevel = LogLevel.Info; + + /// + /// Tag every log so that users know which logs came from Hot Reload + /// + private const string TAG = "[HotReload] "; + + public static void Debug(string message) { + if (minLevel <= LogLevel.Debug) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "{0}{1}", TAG, message); + #else + UnityEngine.Debug.Log(TAG + message); + #endif + } + } + + [StringFormatMethod("message")] + public static void Debug(string message, params object[] args) { + if (minLevel <= LogLevel.Debug) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, TAG + message, args); + #else + UnityEngine.Debug.LogFormat(TAG + message, args); + #endif + } + } + + public static void Info(string message) { + if (minLevel <= LogLevel.Info) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "{0}{1}", TAG, message); + #else + UnityEngine.Debug.Log(TAG + message); + #endif + } + } + + [StringFormatMethod("message")] + public static void Info(string message, params object[] args) { + if (minLevel <= LogLevel.Info) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, TAG + message, args); + #else + UnityEngine.Debug.LogFormat(TAG + message, args); + #endif + } + } + + public static void Warning(string message) { + if (minLevel <= LogLevel.Warning) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, "{0}{1}", TAG, message); + #else + UnityEngine.Debug.LogWarning(TAG + message); + #endif + } + } + + [StringFormatMethod("message")] + public static void Warning(string message, params object[] args) { + if (minLevel <= LogLevel.Warning) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, TAG + message, args); + #else + UnityEngine.Debug.LogWarningFormat(TAG + message, args); + #endif + } + } + + public static void Error(string message) { + if (minLevel <= LogLevel.Error) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Error, LogOption.NoStacktrace, null, "{0}{1}", TAG, message); + #else + UnityEngine.Debug.LogError(TAG + message); + #endif + } + } + + [StringFormatMethod("message")] + public static void Error(string message, params object[] args) { + if (minLevel <= LogLevel.Error) { + #if (UNITY_2019_4_OR_NEWER) + UnityEngine.Debug.LogFormat(LogType.Error, LogOption.NoStacktrace, null, TAG + message, args); + #else + UnityEngine.Debug.LogErrorFormat(TAG + message, args); + #endif + } + } + + public static void Exception(Exception exception) { + if (minLevel <= LogLevel.Exception) { + UnityEngine.Debug.LogException(exception); + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs.meta new file mode 100644 index 0000000..5cf0be6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0719c57eeba24f4cac8d99016ba5b967 +timeCreated: 1674203567 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs b/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs new file mode 100644 index 0000000..e062951 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs @@ -0,0 +1,25 @@ +namespace SingularityGroup.HotReload { + /// + /// The log level enumeration for the Hot Reload package + /// Used in to set the log level. + /// + public enum LogLevel { + /// Debug logs are useful for developers of Hot Reload + Debug = 1, + + /// Info logs potentially useful for users of Hot Reload + Info = 2, + + /// Warnings are visible to users of Hot Reload + Warning = 3, + + /// Errors are visible to users of Hot Reload + Error = 4, + + /// Exceptions are visible to users of Hot Reload + Exception = 5, + + /// No logs are visible to users of Hot Reload + Disabled = 6, + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs.meta new file mode 100644 index 0000000..d74e98d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7b5723ec3c2140041a9c25bbf16eeac4 +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/Runtime/Public/LogLevel.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef b/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef new file mode 100644 index 0000000..834ee8f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef @@ -0,0 +1,3 @@ +{ + "name": "SingularityGroup.HotReload.Runtime.Public" +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef.meta new file mode 100644 index 0000000..26e6063 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: f2be1b7392ef6cc4eafd0ee8ac7a090a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs new file mode 100644 index 0000000..6fcaac6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs @@ -0,0 +1,482 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Localization; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEngine; +using UnityEngine.Networking; + +[assembly: InternalsVisibleTo("CodePatcherEditor")] +[assembly: InternalsVisibleTo("TestProject")] +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.IntegrationTests")] +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.EditorTests")] + +namespace SingularityGroup.HotReload { + class HttpResponse { + public readonly HttpStatusCode statusCode; + public readonly Exception exception; + public readonly string responseText; + + public HttpResponse(HttpStatusCode statusCode, Exception exception, string responseText) { + this.statusCode = statusCode; + this.exception = exception; + this.responseText = responseText; + } + } + + public class ChangelogVersion { + public string versionNum; + public List fixes; + public List improvements; + public string date; + public List features; + public string generalInfo; + } + + static class RequestHelper { + internal const ushort defaultPort = 33242; + internal const string defaultServerHost = "127.0.0.1"; + const string ChangelogURL = PackageConst.DefaultLocale == Locale.SimplifiedChinese ? + "https://d2tc55zjhw51ly.cloudfront.net/releases/latest/changelog-zh.json" : + "https://d2tc55zjhw51ly.cloudfront.net/releases/latest/changelog.json"; + static readonly string defaultOrigin = GetProjectRoot(); + public static string origin { get; private set; } = defaultOrigin; + + static string GetProjectRoot() { + if (MultiplayerPlaymodeHelper.IsClone) { + return Path.GetFullPath(MultiplayerPlaymodeHelper.PathToMainProject(".")); + } + return Path.GetFullPath("."); + } + + static PatchServerInfo serverInfo = new PatchServerInfo(defaultServerHost, null, null); + public static PatchServerInfo ServerInfo => serverInfo; + + static string cachedUrl; + static string url => cachedUrl ?? (cachedUrl = CreateUrl(serverInfo)); + + public static int port => serverInfo?.port ?? defaultPort; + + static readonly HttpClient client = CreateHttpClientWithOrigin(); + // separate client for each long polling request + static readonly HttpClient clientPollPatches = CreateHttpClientWithOrigin(); + static readonly HttpClient clientPollAssets = CreateHttpClientWithOrigin(); + static readonly HttpClient clientPollStatus = CreateHttpClientWithOrigin(); + + static readonly HttpClient[] allClients = new[] { client, clientPollPatches, clientPollAssets, clientPollStatus }; + + static HttpClient CreateHttpClientWithOrigin() { + var httpClient = HttpClientUtils.CreateHttpClient(); + httpClient.DefaultRequestHeaders.Add("origin", origin); + return httpClient; + } + + /// + /// Create url for a hostname and port + /// + internal static string CreateUrl(PatchServerInfo server) { + return $"http://{server.hostName}:{server.port.ToString()}"; + } + + public static PatchServerInfo SetServerPort(int port) { + return SetServerInfo(new PatchServerInfo(serverInfo.hostName, port, serverInfo.commitHash, serverInfo.rootPath, false, serverInfo.customRequestOrigin)); + } + + public static PatchServerInfo SetServerInfo(PatchServerInfo info) { + if (info != null) Log.Debug($"SetServerInfo to {CreateUrl(info)}"); + serverInfo = info; + cachedUrl = null; + + if (info?.customRequestOrigin != null) { + SetOrigin(info.customRequestOrigin); + } + return info; + } + + // This function is not thread safe but is currently called before the first request is sent so no issue. + static void SetOrigin(string newOrigin) { + if (newOrigin == origin) { + return; + } + origin = newOrigin; + + foreach (var httpClient in allClients) { + httpClient.DefaultRequestHeaders.Remove("origin"); + httpClient.DefaultRequestHeaders.Add("origin", newOrigin); + } + } + + static string[] assemblySearchPaths; + public static void ChangeAssemblySearchPaths(string[] paths) { + assemblySearchPaths = paths; + } + + // Don't use for requests to HR server + [UsedImplicitly] + internal static async Task GetAsync(string path) { + using (UnityWebRequest www = UnityWebRequest.Get(path)) { + await SendRequestAsync(www); + + if (string.IsNullOrEmpty(www.error)) { + return www.downloadHandler.text; + } else { + return null; + } + } + } + + internal static Task SendRequestAsync(UnityWebRequest www) { + var req = www.SendWebRequest(); + var tcs = new TaskCompletionSource(); + req.completed += op => tcs.TrySetResult((UnityWebRequestAsyncOperation)op); + return tcs.Task; + } + + static bool pollPending; + internal static async void PollMethodPatches(string lastPatchId, Action onResponseReceived) { + if (pollPending) { + return; + } + pollPending = true; + string[] searchPaths = null; + // This is here so that it doesn't override searchPaths registered by main project + if (!MultiplayerPlaymodeHelper.IsClone) { + searchPaths = assemblySearchPaths ?? CodePatcher.I.GetAssemblySearchPaths(); + } + var body = SerializeRequestBody(new MethodPatchRequest(lastPatchId, searchPaths, TimeSpan.FromSeconds(20), origin)); + + await ThreadUtility.SwitchToThreadPool(); + + try { + var result = await PostJson(url + "/patch", body, 30, overrideClient: clientPollPatches).ConfigureAwait(false); + if(result.statusCode == HttpStatusCode.OK) { + var responses = JsonConvert.DeserializeObject(result.responseText); + await ThreadUtility.SwitchToMainThread(); + foreach(var response in responses) { + onResponseReceived(response); + } + } else if(result.statusCode == HttpStatusCode.Unauthorized || result.statusCode == 0) { + // Server is not running or not authorized. + // We don't want to spam requests in that case. + await Task.Delay(5000); + } else if(result.statusCode == HttpStatusCode.ServiceUnavailable) { + //Server shut down + await Task.Delay(5000); + } else { + Log.Info(Localization.Translations.Logging.PollMethodPatchesFailed, (int)result.statusCode, result.responseText, result.exception); + } + } finally { + pollPending = false; + } + } + + static bool pollPatchStatusPending; + internal static async void PollPatchStatus(Action onResponseReceived, PatchStatus latestStatus) { + if (pollPatchStatusPending) return; + + pollPatchStatusPending = true; + var body = SerializeRequestBody(new PatchStatusRequest(TimeSpan.FromSeconds(20), latestStatus)); + + try { + var result = await PostJson(url + "/patchStatus", body, 30, overrideClient: clientPollStatus).ConfigureAwait(false); + if(result.statusCode == HttpStatusCode.OK) { + var response = JsonConvert.DeserializeObject(result.responseText); + await ThreadUtility.SwitchToMainThread(); + onResponseReceived(response); + } else if(result.statusCode == HttpStatusCode.Unauthorized || result.statusCode == 0) { + // Server is not running or not authorized. + // We don't want to spam requests in that case. + await Task.Delay(5000); + } else if(result.statusCode == HttpStatusCode.ServiceUnavailable) { + //Server shut down + await Task.Delay(5000); + } else { + Log.Info(Localization.Translations.Logging.PollPatchStatusFailed, (int)result.statusCode, result.responseText, result.exception); + } + } finally { + pollPatchStatusPending = false; + } + } + + static bool assetPollPending; + internal static async void PollAssetChanges(Action onResponseReceived) { + if (assetPollPending) return; + + assetPollPending = true; + + try { + await ThreadUtility.SwitchToThreadPool(); + var body = SerializeRequestBody(new AssetChangesRequest(TimeSpan.FromSeconds(20))); + var result = await PostJson(url + "/assetChanges", body, 30, overrideClient: clientPollAssets).ConfigureAwait(false); + + if (result.statusCode == HttpStatusCode.OK) { + var responses = JsonConvert.DeserializeObject>(result.responseText); + await ThreadUtility.SwitchToMainThread(); + // Looping in reverse order fixes moving files: + // by default new files come in before old ones which causes issues because meta files for old location has to be deleted first + for (var i = responses.Count - 1; i >= 0; i--) { + var response = responses[i]; + // Avoid importing assets twice + if (responses.Contains(response + ".meta")) { + Log.Debug($"Ignoring asset change inside Unity: {response}"); + continue; + } + onResponseReceived(response); + } + } else if(result.statusCode == HttpStatusCode.Unauthorized || result.statusCode == 0) { + // Server is not running or not authorized. + // We don't want to spam requests in that case. + await Task.Delay(5000); + } else if(result.statusCode == HttpStatusCode.ServiceUnavailable) { + //Server shut down + await Task.Delay(5000); + } else { + Log.Info(Localization.Translations.Logging.PollAssetChangesFailed, (int)result.statusCode, result.responseText, result.exception); + } + } finally { + assetPollPending = false; + } + } + + public static async Task RequestFlushErrors(int timeoutSeconds = 30) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var resp = await PostJson(CreateUrl(serverInfo) + "/flush", "", timeoutSeconds, cts.Token); + if (resp.statusCode == HttpStatusCode.OK) { + try { + return JsonConvert.DeserializeObject(resp.responseText); + } catch { + return null; + } + } + return null; + } + + public static async Task RequestEditorsWithoutHRRunning(int timeoutSeconds = 30) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var resp = await PostJson(CreateUrl(serverInfo) + "/editorsWithoutHR", "", timeoutSeconds, cts.Token); + if (resp.statusCode == HttpStatusCode.OK) { + try { + return JsonConvert.DeserializeObject(resp.responseText); + } catch { + return null; + } + } + return null; + } + + public static async Task RequestLogin(string email, string password, int timeoutSeconds) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var json = SerializeRequestBody(new Dictionary { + { "email", email }, + { "password", password }, + }); + var resp = await PostJson(url + "/login", json, timeoutSeconds, cts.Token); + if (resp.exception == null) { + return JsonConvert.DeserializeObject(resp.responseText); + } else { + return LoginStatusResponse.FromRequestError($"{resp.exception.GetType().Name} {resp.exception.Message}"); + } + } + + public static async Task GetLoginStatus(int timeoutSeconds) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var resp = await PostJson(url + "/status", string.Empty, timeoutSeconds, cts.Token); + if (resp.exception == null) { + return JsonConvert.DeserializeObject(resp.responseText); + } else { + return LoginStatusResponse.FromRequestError($"{resp.exception.GetType().Name} {resp.exception.Message}"); + } + } + + internal static async Task RequestLogout(int timeoutSeconds = 10) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var resp = await PostJson(CreateUrl(serverInfo) + "/logout", "", timeoutSeconds, cts.Token); + if (resp.statusCode == HttpStatusCode.OK) { + try { + return JsonConvert.DeserializeObject(resp.responseText); + } catch (Exception ex) { + return LoginStatusResponse.FromRequestError(string.Format(Localization.Translations.Logging.DeserializingResponseFailed, ex.GetType().Name, ex.Message)); + } + } else { + return LoginStatusResponse.FromRequestError(resp.responseText ?? Localization.Translations.Logging.RequestTimeout); + } + } + + internal static async Task RequestActivatePromoCode(string promoCode, int timeoutSeconds = 20) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + await ThreadUtility.SwitchToThreadPool(); + try { + using (var resp = await client.PostAsync(CreateUrl(serverInfo) + "/activatePromoCode", new StringContent(promoCode), cts.Token).ConfigureAwait(false)) { + var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + try { + return JsonConvert.DeserializeObject(str); + } catch { + return null; + } + } + } catch { + return null; + } + } + + internal static async Task RequestEditorEventWithRetry(Stat stat, EditorExtraData extraData = null) { + if (MultiplayerPlaymodeHelper.IsClone) { + return; + } + int attempt = 0; + do { + var resp = await RequestHelper.RequestEditorEvent(stat, extraData); + if (resp.statusCode == HttpStatusCode.OK) { + return; + } + await Task.Delay(TimeSpan.FromMilliseconds(200)); + } while (attempt++ < 10000); + } + + internal static Task RequestEditorEvent(Stat stat, EditorExtraData extraData = null) { + var body = SerializeRequestBody(new EditorEventRequest(stat, extraData)); + return PostJson(url + "/editorEvent", body, int.MaxValue); + } + + public static async Task KillServer() { + await ThreadUtility.SwitchToThreadPool(); + await KillServerInternal().ConfigureAwait(false); + } + + internal static async Task RegisterClone() { + await ThreadUtility.SwitchToThreadPool(); + try { + var body = SerializeRequestBody(new RegisterCloneRequest(Process.GetCurrentProcess().Id)); + using(await client.PostAsync(CreateUrl(serverInfo) + "/registerClone", new StringContent(body)).ConfigureAwait(false)) { } + } catch { + //ignored + } + } + + internal static async Task KillServerInternal() { + try { + using(await client.PostAsync(CreateUrl(serverInfo) + "/kill", new StringContent(origin)).ConfigureAwait(false)) { } + } catch { + //ignored + } + } + + public static async Task PingServer(Uri uri) { + await ThreadUtility.SwitchToThreadPool(); + + try { + using (var resp = await client.GetAsync(uri).ConfigureAwait(false)) { + return resp.StatusCode == HttpStatusCode.OK; + } + } catch { + return false; + } + } + + public static bool IsReleaseMode() { +# if (UNITY_EDITOR && UNITY_2022_1_OR_NEWER) + return UnityEditor.Compilation.CompilationPipeline.codeOptimization == UnityEditor.Compilation.CodeOptimization.Release; +# elif (UNITY_EDITOR) + return false; +# elif (DEBUG) + return false; +# else + return true; +#endif + } + + public static Task RequestClearPatches() { + var body = SerializeRequestBody(new CompileRequest(serverInfo.rootPath, IsReleaseMode())); + return PostJson(url + "/clearpatches", body, 10); + } + + public static async Task RequestCompile(Action onResponseReceived) { + var body = SerializeRequestBody(new CompileRequest(serverInfo.rootPath, IsReleaseMode())); + var result = await PostJson(url + "/compile", body, 10); + if (result.statusCode == HttpStatusCode.OK && !string.IsNullOrEmpty(result.responseText)) { + var responses = JsonConvert.DeserializeObject>(result.responseText); + if (responses == null) { + return; + } + await ThreadUtility.SwitchToMainThread(); + foreach (var response in responses) { + // Avoid importing assets twice + if (responses.Contains(response + ".meta")) { + Log.Debug($"Ignoring asset change inside Unity: {response}"); + continue; + } + onResponseReceived(response); + } + } + } + + internal static async Task> FetchChangelog(int timeoutSeconds = 20) { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + await ThreadUtility.SwitchToThreadPool(); + + try { + using(var resp = await client.GetAsync(ChangelogURL, cts.Token).ConfigureAwait(false)) { + var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + if (resp.StatusCode == HttpStatusCode.OK) { + return JsonConvert.DeserializeObject>(str); + } + return new List(); + } + } catch { + return new List(); + } + } + + [UsedImplicitly] + internal static async Task Post(string route, string json) { + var resp = await PostJson(url + route, json, 10); + return resp.statusCode == HttpStatusCode.OK; + } + + internal static async Task RequestHandshake(PatchServerInfo info, HashSet defineSymbols, string projectExclusionRegex) { + await ThreadUtility.SwitchToThreadPool(); + + var body = SerializeRequestBody(new MobileHandshakeRequest(defineSymbols, projectExclusionRegex)); + + var requestUrl = CreateUrl(info) + "/handshake"; + Log.Debug($"RequestHandshake to {requestUrl}"); + var resp = await PostJson(requestUrl, body, 120).ConfigureAwait(false); + if (resp.statusCode == HttpStatusCode.OK) { + return JsonConvert.DeserializeObject(resp.responseText); + } else if(resp.statusCode == HttpStatusCode.ServiceUnavailable) { + return new MobileHandshakeResponse(null, ServerHandshake.Result.WaitForCompiling.ToString()); + } else { + return new MobileHandshakeResponse(null, resp.responseText); + } + } + + static string SerializeRequestBody(T request) { + return JsonConvert.SerializeObject(request); + } + + static async Task PostJson(string uri, string json, int timeoutSeconds, CancellationToken token = default(CancellationToken), HttpClient overrideClient = null) { + var httpClient = overrideClient ?? client; + await ThreadUtility.SwitchToThreadPool(); + + try { + var content = new StringContent(json, Encoding.UTF8, "application/json"); + using(var resp = await httpClient.PostAsync(uri, content, token).ConfigureAwait(false)) { + var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + return new HttpResponse(resp.StatusCode, null, str); + } + } catch(Exception ex) { + return new HttpResponse(0, ex, null); + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs.meta new file mode 100644 index 0000000..9cb2b8e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 645498c7b1683f44b90634228dbf1626 +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/Runtime/RequestHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs new file mode 100644 index 0000000..dddc677 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs @@ -0,0 +1,244 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + internal class ServerHandshake { + public static readonly ServerHandshake I = new ServerHandshake(); + + /// + /// Not verified as compatible yet - need to do handshake + /// + private PatchServerInfo pendingServer; + + /// + /// Handshake is complete. Player can connect to this server. + /// + private PatchServerInfo verifiedServer; + + private Task handshakeCheck; + + private CancellationTokenSource cts = new CancellationTokenSource(); + /// Track first handshake request after calling SetServerInfo. + /// Sometimes and it can take 10-30 seconds and succeed. + private TaskCompletionSource firstHandshake = new TaskCompletionSource(); + + /// Server info should be well known or a strong guess, not just a random ip address. + public Task SetServerInfo(PatchServerInfo serverInfo) { + if (verifiedServer != null && serverInfo == verifiedServer) { + return Task.FromResult(Result.Verified); + } + pendingServer = serverInfo; + if (serverInfo != null) { + Prompts.SetConnectionState(ConnectionSummary.Handshaking); + } + + // disconnect + verifiedServer = null; + + // cancel any ongoing RequestHandshake task + firstHandshake.TrySetCanceled(cts.Token); + firstHandshake = new TaskCompletionSource(); + cts.Cancel(); + cts = new CancellationTokenSource(); + if (serverInfo == null) return Task.FromResult(Result.None); + return firstHandshake.Task; + } + + /// Ensures a handshake request is running. + public void CheckHandshake() { + var serverToCheck = pendingServer; + if (verifiedServer == null && serverToCheck != null) { + if (handshakeCheck == null || handshakeCheck.IsCompleted) { + handshakeCheck = Task.Run(async () => { + try { + Log.Debug("Run RequestHandshake"); + var results = await RequestHandshake(serverToCheck); + await ThreadUtility.SwitchToMainThread(); + var decisionIsFinal = await VerifyResults(results, serverToCheck); + firstHandshake.TrySetResult(results); // VerifyResults() can also set it, this is the default fallback + if (decisionIsFinal) { + pendingServer = null; + } + } catch (Exception ex) { + Log.Exception(ex); + } finally { + // set as failed if wasnt set as true by above code + firstHandshake.TrySetResult(Result.None); + } + }, cts.Token); + } + } + } + + /// + /// Verify results of the handshake. + /// + /// + /// + /// True if the conclusion is final, otherwise false + /// + /// Must be called on main thread because it uses Unity UI methods. + /// + async Task VerifyResults(Result results, PatchServerInfo server) { + if (results.HasFlag(Result.QuietWarning)) { + // can handle here if needed later + } + if (results.HasFlag(Result.Verified)) { + if (!firstHandshake.Task.IsCompleted) { + Prompts.SetConnectionState(ConnectionSummary.Connecting); + } + OnVerified(server); + return true; + } + + // handle objections in order of obviousness, most obvious goes first + if (results.HasFlag(Result.DifferentProject)) { + await Prompts.ShowQuestionDialog(new QuestionDialog.Config { + summary = Localization.Translations.Dialogs.DifferentProjectSummary, + suggestion = Localization.Translations.Dialogs.DifferentProjectSuggestion, + continueButtonText = "OK", + cancelButtonText = null, + }); + // they need to provide a new server info + Prompts.SetConnectionState(ConnectionSummary.Cancelled); + return true; + } + if (results.HasFlag(Result.DifferentCommit)) { + Prompts.SetConnectionState(ConnectionSummary.DifferencesFound); + bool yes = await Prompts.ShowQuestionDialog(new QuestionDialog.Config { + summary = Localization.Translations.Dialogs.DifferentCommitSummary, + suggestion = Localization.Translations.Dialogs.DifferentCommitSuggestion, + continueButtonText = "Connect", + }); + if (yes) { + results |= Result.Verified; + Prompts.SetConnectionState(ConnectionSummary.Connecting); + firstHandshake.TrySetResult(results); + OnVerified(server); + } else { + Prompts.SetConnectionState(ConnectionSummary.Cancelled); + } + // cancel -> tell them to provide a new server + return true; + } + + if (results.HasFlag(Result.TempError)) { + // retry might work, its not over yet + return false; + } + // at time of writing, code should never reach here. Adding new HandshakeResult flags should be handled above. + Log.Debug("UNEXPECTED: VerifyResults continued into untested code: {0}", results); + return true; + } + + void OnVerified(PatchServerInfo serverToCheck) { + verifiedServer = serverToCheck; + } + + public bool TryGetVerifiedServer(out PatchServerInfo serverInfo) { + // take verifiedServer + var server = Interlocked.Exchange(ref verifiedServer, null); + serverInfo = server; + return serverInfo != null; + } + + /// + /// Result of a handshake with the remote Hot Reload instance. + /// + [Flags] + public enum Result { + None = 0, + DifferentCommit = 1 << 0, + DifferentProject = 1 << 1, + + /// + /// A temporary error occurred, retrying might work. + /// + TempError = 1 << 2, + + /// + /// Hot Reload is compiling, so we should wait a bit before trying again. + /// + WaitForCompiling = 1 << 3, + + [Obsolete("Not needed so far", true)] + Placeholder2 = 1 << 4, + + // use when a warning is logged, but we're allowing Hot Reload to connect bcus it probably works. + QuietWarning = 1 << 5, + Verified = 1 << 6, + } + + static async Task RequestHandshake(PatchServerInfo info) { + var buildInfo = PlayerEntrypoint.PlayerBuildInfo; + var results = Result.None; + var verified = true; + Log.Debug($"Comparing commits {buildInfo.commitHash} and {info.commitHash}"); + if (buildInfo.IsDifferentCommit(info.commitHash)) { + results |= Result.DifferentCommit; + verified = false; + } + // Check for health before sending handshake request + // If health check fails UI updates faster + var healthy = await ServerHealthCheck.CheckHealthAsync(info); + if (!healthy) { + Log.Debug("Won't send handshake request because server is not healhy"); + return results; + } + Log.Info(string.Format(Localization.Translations.Logging.RequestHandshakeToServer, info.hostName)); + //Log.Debug("Handshake with projectOmissionRegex: \"{0}\"", buildInfo.projectOmissionRegex); + var response = await RequestHelper.RequestHandshake(info, buildInfo.DefineSymbolsAsHashSet, + buildInfo.projectOmissionRegex); + if (response.error != null) { + verified = false; + Log.Debug($"RequestHandshake errored: {response.error}"); + if (response.error == Result.WaitForCompiling.ToString()) { + // WaitForCompiling is a temp error + results |= Result.WaitForCompiling; + results |= Result.TempError; + } else { + results |= Result.TempError; + } + } + + if (response.data == null) { + // need response data to continue + verified = false; + return results; + } + + // handshake response is what we post to /files which is BuildInfo + var remoteBuildTarget = response.data[nameof(BuildInfo.activeBuildTarget)] as string; + var remoteCommitHash = response.data[nameof(BuildInfo.commitHash)] as string; + var remoteProjectIdentifier = response.data[nameof(BuildInfo.projectIdentifier)] as string; + if (buildInfo.IsDifferentCommit(remoteCommitHash)) { + Log.Debug($"RequestHandshake server is on different commit {response.error}"); + results |= Result.DifferentCommit; + verified = false; + } + + if (remoteProjectIdentifier != buildInfo.projectIdentifier) { + Log.Debug("RequestHandshake remote is using a different project identifier"); + results |= Result.DifferentProject; + verified = false; + } + + if (remoteBuildTarget == null) { + // Should never happen. Server responsed with an error when no BuildInfo at all. + Log.Warning(string.Format(Localization.Translations.Errors.HandshakeFailedInvalidBuildTarget, buildInfo.activeBuildTarget)); + results |= Result.QuietWarning; + } else if (remoteBuildTarget != buildInfo.activeBuildTarget) { + Log.Warning(string.Format(Localization.Translations.Errors.BuildTargetMismatch, remoteBuildTarget, buildInfo.activeBuildTarget)); + results |= Result.QuietWarning; + } + + if (verified) { + results |= Result.Verified; + } + return results; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs.meta new file mode 100644 index 0000000..796bbbe --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 2b0e8e99bb55456888ff440a83e3e082 +timeCreated: 1674459557 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs new file mode 100644 index 0000000..0b077af --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; + +namespace SingularityGroup.HotReload { + internal class ServerHealthCheck : IServerHealthCheck { + private static readonly TimeSpan heartBeatTimeout = TimeSpan.FromMilliseconds(5001); + public static readonly ServerHealthCheck I = new ServerHealthCheck(); + + private Uri healthCheckEndpoint = null; + private Task healthCheck = null; + private DateTime healthOkayAt = DateTime.MinValue; + + public void SetServerInfo(PatchServerInfo serverInfo) { + if (serverInfo == null) { + Log.Debug("ServerHealthCheck SetServerInfo to null"); + healthCheckEndpoint = null; + } else { + var url = RequestHelper.CreateUrl(serverInfo) + "/ping"; + Log.Debug("ServerHealthCheck SetServerInfo using url {0}", url); + healthCheckEndpoint = new Uri(url); + } + healthCheck = null; + healthOkayAt = DateTime.MinValue; + } + + public bool IsServerHealthy => DateTime.UtcNow - healthOkayAt < heartBeatTimeout; + + /// Is it confirmed the server has been running before? + public bool WasServerResponding => healthOkayAt != DateTime.MinValue; + + // any thread + public async Task CheckHealthAsync() { + if (healthCheckEndpoint == null + // wait for existing healthcheck to finish + || healthCheck?.IsCompleted == false + ) { + return; + } + healthCheck = CheckHealthAsync(healthCheckEndpoint); + if (await healthCheck) { + healthOkayAt = DateTime.UtcNow; + } + } + + public static async Task CheckHealthAsync(PatchServerInfo info) { + var url = RequestHelper.CreateUrl(info) + "/ping"; + return await CheckHealthAsync(new Uri(url)); + } + + public static async Task CheckHealthAsync(Uri uri) { + var ping = RequestHelper.PingServer(uri); + await Task.WhenAny(ping, Task.Delay(heartBeatTimeout)); + return ping.IsCompleted && ping.Result; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs.meta new file mode 100644 index 0000000..9f3daed --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6acd15aa18d34f219245201f66ac0048 +timeCreated: 1674458040 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef b/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef new file mode 100644 index 0000000..9affeb5 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef @@ -0,0 +1,29 @@ +{ + "name": "SingularityGroup.HotReload.Runtime", + "rootNamespace": "SingularityGroup.HotReload", + "references": [ + "GUID:343deaaf83e0cee4ca978e7df0b80d21", + "GUID:2bafac87e7f4b9b418d9448d219b01ab", + "GUID:46c537318f0d530469a1df1fafe86c9c", + "GUID:af67dfb2ec5c9c740be50ae4470ed85f", + "SingularityGroup.HotReload.Runtime.Public", + "Unity.InputSystem" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "SingularityGroup.HotReload.RuntimeDependencies.dll", + "SingularityGroup.HotReload.RuntimeDependencies2019.dll", + "SingularityGroup.HotReload.RuntimeDependencies2022.dll", + "SingularityGroup.HotReload.RuntimeDependencies2020.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "ENABLE_MONO", + "DEVELOPMENT_BUILD || UNITY_EDITOR" + ], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef.meta b/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef.meta new file mode 100644 index 0000000..1ec35dd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 46b7fe7fb55a0ca4aa66f90961731b83 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs new file mode 100644 index 0000000..ff33f09 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.RuntimeDependencies; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + internal class SymbolResolver { + readonly Dictionary> assembliesByName; + + public SymbolResolver(Dictionary> assembliesByName) { + this.assembliesByName = assembliesByName; + } + + public void AddAssembly(Assembly asm) { + var asmName = asm.GetNameSafe(); + List assemblies; + if(!assembliesByName.TryGetValue(asmName, out assemblies)) { + assembliesByName.Add(asmName, assemblies = new List()); + } + assemblies.Add(asm); + } + + public Type Resolve(SType t) { + var assmeblies = Resolve(t.assemblyName); + Type result = null; + Exception lastException = null; + for (var i = 0; i < assmeblies.Count; i++) { + try { + result = assmeblies[i].GetLoadedModules()[0].ResolveType(t.metadataToken); + if (t.isGenericParameter) { + if (!result.IsGenericTypeDefinition) { + throw new SymbolResolvingFailedException(t, new ApplicationException(Localization.Translations.Errors.GenericParameterNotGenericType)); + } + var genericParameters = result.GetGenericArguments(); + if (t.genericParameterPosition >= genericParameters.Length) { + throw new SymbolResolvingFailedException(t, new ApplicationException(Localization.Translations.Errors.GenericParameterDidNotExist)); + } + result = genericParameters[t.genericParameterPosition]; + } + break; + } catch(Exception ex) { + lastException = ex; + } + } + if(result == null) { + throw new SymbolResolvingFailedException(t, lastException); + } + return result; + } + + public FieldInfo Resolve(SField t) { + var assmeblies = Resolve(t.assemblyName); + FieldInfo result = null; + Exception lastException = null; + for (var i = 0; i < assmeblies.Count; i++) { + try { + result = assmeblies[i].GetLoadedModules()[0].ResolveField(t.metadataToken); + break; + } catch(Exception ex) { + lastException = ex; + } + } + if(result == null) { + throw new SymbolResolvingFailedException(t, lastException); + } + return result; + } + + public IReadOnlyList Resolve(string assembly) { + List list; + if(assembliesByName.TryGetValue(assembly, out list)) { + return list; + } + return Array.Empty(); + } + + public MethodBase Resolve(SMethod m) { + var assmeblies = Resolve(m.assemblyName); + MethodBase result = null; + Exception lastException = null; + for (var i = 0; i < assmeblies.Count; i++) { + try { + result = assmeblies[i].GetLoadedModules()[0].ResolveMethod(m.metadataToken); + break; + } catch(Exception ex) { + lastException = ex; + } + } + if(result == null) { + throw new SymbolResolvingFailedException(m, lastException); + } + return result; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs.meta new file mode 100644 index 0000000..a28408b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 90f4f3b01fb1917448d68d9477a7c932 +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/Runtime/SymbolResolver.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs new file mode 100644 index 0000000..be4c1f8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs @@ -0,0 +1,16 @@ +using System; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + internal class SymbolResolvingFailedException : Exception { + public SymbolResolvingFailedException(SMethod m, Exception inner) + : base(string.Format(Localization.Translations.Errors.UnableToResolveMethodInAssembly, m.displayName, m.assemblyName), inner) { } + + public SymbolResolvingFailedException(SType t, Exception inner) + : base(string.Format(Localization.Translations.Errors.UnableToResolveTypeInAssembly, t.typeName, t.assemblyName), inner) { } + + public SymbolResolvingFailedException(SField t, Exception inner) + : base(string.Format(Localization.Translations.Errors.UnableToResolveFieldInAssembly, t.fieldName, t.assemblyName), inner) { } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs.meta new file mode 100644 index 0000000..474be69 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68483a4d9ffc894488e49faee539ab57 +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/Runtime/SymbolResolvingFailedException.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs new file mode 100644 index 0000000..2889359 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; +using SingularityGroup.HotReload.Localization; + +namespace SingularityGroup.HotReload { + internal static class TaskExtensions { + public static async void Forget(this Task task, CancellationToken token = new CancellationToken()) { + try { + await task; + if(task.IsFaulted) { + throw task.Exception ?? new Exception(Localization.Translations.Common.UnknownException + " " + task); + } + token.ThrowIfCancellationRequested(); + } + catch(OperationCanceledException) { + // ignore + } catch(Exception ex) { + if(!token.IsCancellationRequested) { + Log.Exception(ex); + } + } + } + + /// + /// Blocks until condition is true or timeout occurs. + /// + /// The break condition. + /// The frequency at which the condition will be checked. + /// The timeout in milliseconds. + /// True on condition became true, False if timeouted + // credit: https://stackoverflow.com/a/52357854/5921285 + public static async Task WaitUntil(Func condition, int timeoutMs = -1, int pollInterval = 33) { + var waitTask = Task.Run(async () => { + while (!condition()) await Task.Delay(pollInterval); + }); + + if (waitTask != await Task.WhenAny(waitTask, + Task.Delay(timeoutMs))) { + // timed out + return false; + } + return true; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs.meta new file mode 100644 index 0000000..e01c00a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 10049b5f598dc924d99daeafd50bce16 +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/Runtime/TaskExtensions.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs new file mode 100644 index 0000000..07669c1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs @@ -0,0 +1,224 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using UnityEngine; + +namespace SingularityGroup.HotReload { +#if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] +#endif + static class ThreadUtility { + /// + /// Run code on Unity's main thread + /// + /// + /// This field is set early in [InitializeOnLoadMethod] in the editor and [RuntimeInitializeOnLoad] in playmode / for player builds, so your code assume it is already set. + /// +#if UNITY_EDITOR + static SynchronizationContext _cachedMainContext; + public static SynchronizationContext MainContext + { + get { + if(_cachedMainContext != null) { + return _cachedMainContext; + } + return EditorFallbackContext.I; + } + private set { + _cachedMainContext = value; + } + } + + class EditorFallbackContext : SynchronizationContext { + public static readonly EditorFallbackContext I = new EditorFallbackContext(); + EditorFallbackContext() { } + + public override void Send(SendOrPostCallback d, object state) { + UnityEditor.EditorApplication.delayCall += () => d(state); + } + public override void Post(SendOrPostCallback d, object state) { + UnityEditor.EditorApplication.delayCall += () => d(state); + } + } +#else + public static SynchronizationContext MainContext {get; private set;} +#endif + + public static int mainThreadId {get; private set;} + +#if UNITY_EDITOR + static ThreadUtility() { +#else + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void InitMainThread() { +#endif + MainContext = SynchronizationContext.Current; + mainThreadId = Thread.CurrentThread.ManagedThreadId; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void InitEditor() { + //trigger static constructor + } + + public static bool ShouldLogException(Exception ex) { + AggregateException agg; + while((agg = ex as AggregateException) != null) { + ex = agg.InnerException; + } + if(ex is ThreadAbortException) { + return false; + } + return true; + } + + public static void LogException(Exception ex, CancellationToken token = default(CancellationToken)) { + if(ShouldLogException(ex) && !token.IsCancellationRequested) { + Log.Exception(ex); + } + } + + public static void RunOnMainThread(Action action, CancellationToken token = default(CancellationToken)) { + if(Thread.CurrentThread.ManagedThreadId == mainThreadId) { + action(); + } else { + MainContext.Post(_ => { + if(!token.IsCancellationRequested) { + action(); + } + }, null); + } + } + + public static SwitchToMainThreadAwaitable SwitchToMainThread() { + return new SwitchToMainThreadAwaitable(); + } + + public static CancellableSwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken token) { + return new CancellableSwitchToMainThreadAwaitable(token); + } + + public static SwitchToThreadPoolAwaitable SwitchToThreadPool() { + return new SwitchToThreadPoolAwaitable(); + } + + public static CancellableSwitchToThreadPoolAwaitable SwitchToThreadPool(CancellationToken token) { + return new CancellableSwitchToThreadPoolAwaitable(token); + } + } + + struct SwitchToMainThreadAwaitable { + public Awaiter GetAwaiter() => new Awaiter(); + + public struct Awaiter : ICriticalNotifyCompletion { + static readonly SendOrPostCallback switchToCallback = Callback; + + public bool IsCompleted => Thread.CurrentThread.ManagedThreadId == ThreadUtility.mainThreadId; + + public void GetResult() { } + + public void OnCompleted(Action continuation) { + ThreadUtility.MainContext.Post(switchToCallback, continuation); + } + + public void UnsafeOnCompleted(Action continuation) { + ThreadUtility.MainContext.Post(switchToCallback, continuation); + } + + static void Callback(object state) { + var continuation = (Action)state; + continuation(); + } + } + } + + + struct CancellableSwitchToMainThreadAwaitable { + readonly CancellationToken token; + public CancellableSwitchToMainThreadAwaitable(CancellationToken token) { + this.token = token; + } + + public Awaiter GetAwaiter() => new Awaiter(token); + + public struct Awaiter : ICriticalNotifyCompletion { + readonly CancellationToken token; + public Awaiter(CancellationToken token) { + this.token = token; + } + + public bool IsCompleted => Thread.CurrentThread.ManagedThreadId == ThreadUtility.mainThreadId; + + public void GetResult() { } + + public void OnCompleted(Action continuation) { + UnsafeOnCompleted(continuation); + } + + public void UnsafeOnCompleted(Action continuation) { + var tokenCopy = this.token; + ThreadUtility.MainContext.Post(o => { + if(!tokenCopy.IsCancellationRequested) { + continuation(); + } + }, null); + } + } + } + + struct CancellableSwitchToThreadPoolAwaitable { + readonly CancellationToken token; + public CancellableSwitchToThreadPoolAwaitable(CancellationToken token) { + this.token = token; + } + + public Awaiter GetAwaiter() => new Awaiter(token); + + public struct Awaiter : ICriticalNotifyCompletion { + readonly CancellationToken token; + public Awaiter(CancellationToken token) { + this.token = token; + } + public bool IsCompleted => false; + public void GetResult() { } + + public void OnCompleted(Action continuation) { + ThreadPool.UnsafeQueueUserWorkItem(Callback, continuation); + } + + public void UnsafeOnCompleted(Action continuation) { + ThreadPool.UnsafeQueueUserWorkItem(Callback, continuation); + } + + void Callback(object state) { + token.ThrowIfCancellationRequested(); + var continuation = (Action)state; + continuation(); + } + } + } + + struct SwitchToThreadPoolAwaitable { + public Awaiter GetAwaiter() => new Awaiter(); + + public struct Awaiter : ICriticalNotifyCompletion { + static readonly WaitCallback switchToCallback = Callback; + + public bool IsCompleted => false; + public void GetResult() { } + + public void OnCompleted(Action continuation) { + ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation); + } + + public void UnsafeOnCompleted(Action continuation) { + ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation); + } + + static void Callback(object state) { + var continuation = (Action)state; + continuation(); + } + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs.meta new file mode 100644 index 0000000..f8142fc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 833483c363c74b21a42c9c407d32fd93 +timeCreated: 1673880448 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs new file mode 100644 index 0000000..8b12fbb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace SingularityGroup.HotReload { + static class UnityHelper { + static string m_DataPath; + public static string DataPath { get { Init(); return m_DataPath; } } + + static string m_PersistentDataPath; + public static string PersistentDataPath { get { Init(); return m_PersistentDataPath; } } + + static string m_TemporaryCachePath; + public static string TemporaryCachePath { get { Init(); return m_TemporaryCachePath; } } + + static string m_StreamingAssetsPath; + public static string StreamingAssetsPath { get { Init(); return m_StreamingAssetsPath; } } + + static string m_OperatingSystem; + public static string OperatingSystem { get { Init(); return m_OperatingSystem; } } + + static RuntimePlatform m_Platform; + public static RuntimePlatform Platform { get { Init(); return m_Platform; } } + + static bool m_IsEditor; + public static bool IsEditor { get { Init(); return m_IsEditor; } } + + static bool initialized; + public static void Init() { + if(initialized) return; + m_DataPath = Application.dataPath; + m_PersistentDataPath = Application.persistentDataPath; + m_StreamingAssetsPath = Application.streamingAssetsPath; + m_TemporaryCachePath = Application.temporaryCachePath; + m_OperatingSystem = SystemInfo.operatingSystem; + m_Platform = Application.platform; + m_IsEditor = Application.isEditor; + + initialized = true; + } + } +} diff --git a/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs.meta b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs.meta new file mode 100644 index 0000000..f5a37b1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 0edc320a85054b85ab1e4916bf8176ba +timeCreated: 1674382672 +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Server.meta b/Packages/com.singularitygroup.hotreload/Server.meta new file mode 100644 index 0000000..3703025 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b14aa074bcada145bbd7f88f364c707 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql b/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql new file mode 100644 index 0000000..41674e3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql @@ -0,0 +1,99 @@ +""" +The model of the config file that can optionally be added in the unity project directory. +The name of the file shall be 'hot-reload-config.json' +Example json contents: +{ + "externalSolutions": [ + "../src/Serialization/Serialization.sln", + "../src/WebSocket/WebSocket.sln" + ], + "additionalSourceDirectories": [ + "../src/SharedUtility", + "../src/UnitTestBoilerplate" + ], + "projectBlacklist": [ + "../src/UnitTests/UnitTests.csproj" + ], + "polyfillSourceFiles": [ + "Polyfills/CompilerSupport.cs", + "Polyfills/IndexRange.cs" + ] +} +""" +input HotReloadConfig { + """ + The file paths to external solutions. + Paths shall be specified relative to the unity project path. + + Consider the following example: + + UnityProject + |_ UnityProject.sln + src + |_ ExternalSlnDir + |_ External.sln + + here the path would be '../src/ExternalSlnDir/External.sln' + """ + externalSolutions: [String!] + + """ + The file paths to additional source directories that are not already part of a solution folder or its sub folders. + Paths shall be specified relative to the unity project path. + + Consider this example: + + UnityProject + |_ UnityProject.sln + src + |_ ExternalSlnDir + |_ External.sln + |_ External.csproj <- uses Foo.cs + |_ SharedUtility + |_ Foo.cs + + In such a case the path '../src/SharedUtility' needs to be specified as an additional source directory. + This is mainly to ensure that the file watcher listens to all c# files that are related to the unity project. + The Assets/ and Packages/ folders and all paths to local packages specified in the Packages/manifest.json are already covered. + """ + additionalSourceDirectories: [String!] + + """ + The file paths to project files that Hot Reload should ignore. + This is only needed when you add additional project files to the unity project solution. + Paths shall be specified relative to the unity project path. + + Consider this example: + + UnityProject + |_ UnityProject.sln + src + |_ ExternalSlnDir + |_ External.sln + |_ UnitTests + |_ UnitTests.csproj + + here the path would be '../src/ExternalSlnDir/UnitTests/UnitTests.csproj' + """ + projectBlacklist: [String!] + + + """ + The file paths to source files that should get added to all unity .csproj files. + Use this is you e.g. use a csc.rsp to include polyfill csharp files when compiling the script assemblies + Paths shall be specified relative to the unity project path. + + Consider this example: + + UnityProject + |_ UnityProject.sln + PolyFills + |_ IndexRange.cs + + here the path would be 'PolyFills/IndexRange.cs' + """ + polyfillSourceFiles: [String!] +} + +scalar String + diff --git a/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql.meta b/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql.meta new file mode 100644 index 0000000..9f3f714 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 1860afd846d14904e8e3b2ef393b11a2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Server/linux-x64.meta b/Packages/com.singularitygroup.hotreload/Server/linux-x64.meta new file mode 100644 index 0000000..db93a91 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fdab353f2769bba47aa3bf92f09515c6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh b/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh new file mode 100644 index 0000000..46dc4d2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +set -e + +CLIARGUMENTS_FILE="" +EXECUTABLESOURCEDIR="" +EXECUTABLETARGETDIR="" +TITLE="" +METHODPATCHDIR="" +PIDFILE="" + +while [ "$1" != "" ]; do + case $1 in + -c | --cli-arguments-file ) + shift + CLIARGUMENTS_FILE="$1" + ;; + --executables-source-dir ) + shift + EXECUTABLESOURCEDIR="$1" + ;; + --executable-taget-dir ) + shift + EXECUTABLETARGETDIR="$1" + ;; + --title ) + shift + TITLE="$1" + ;; + --create-no-window ) + shift + CREATENOWINDOW="$1" + ;; + -p | --pidfile ) + shift + PIDFILE="$1" + ;; + -m | --method-patch-dir ) + shift + METHODPATCHDIR="$1" + ;; + esac + shift +done + +if [ -z "/tmp/HotReloadTemp" ] || [ -z "$CLIARGUMENTS_FILE" ] || [ -z "$EXECUTABLESOURCEDIR" ] || [ -z "$EXECUTABLETARGETDIR" ] || [ -z "$TITLE" ] || [ -z "$PIDFILE" ] || [ -z "$METHODPATCHDIR" ] || [ -z "$CREATENOWINDOW" ]; then + echo "Missing arguments" + exit 1 +fi + +CLIARGUMENTS=$(cat "$CLIARGUMENTS_FILE") +rm "$CLIARGUMENTS_FILE" + +rm -rf "$METHODPATCHDIR" +SERVER="$EXECUTABLETARGETDIR/CodePatcherCLI" + +TERMINALRUNSCRIPT="$EXECUTABLESOURCEDIR/terminal-run.sh" +sed -i 's/\r//g' "$TERMINALRUNSCRIPT" + +chmod +x "$TERMINALRUNSCRIPT" +chmod +x "$SERVER" + +HAVETERMINAL="" +"$TERMINALRUNSCRIPT" && HAVETERMINAL="yes" + +INTERNALSCRIPT="$EXECUTABLETARGETDIR/hotreload-internal-start" + +# see doc/linux-system-freeze.org why I put the nice + +cat << EOF > "$INTERNALSCRIPT" +#!/bin/sh +echo \$\$ > "$PIDFILE" +nice -n 5 "$SERVER" $CLIARGUMENTS || read +EOF + +chmod +x "$INTERNALSCRIPT" + +if [[ -n "$HAVETERMINAL" && "$CREATENOWINDOW" != "True" ]]; then + "$TERMINALRUNSCRIPT" "$TITLE" "$INTERNALSCRIPT" +else + printf "Don't have a terminal to run, printing to unity console instead. Consider hacking:\n%s\n" "$TERMINALRUNSCRIPT" + exec "$INTERNALSCRIPT" +fi diff --git a/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh.meta b/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh.meta new file mode 100644 index 0000000..061f4fe --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 69a1a07fd70ddb97f9c75ee89579d1ea +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh b/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh new file mode 100644 index 0000000..39fee8d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Run a terminal with title and command + +# User can already hack this file with their own terminal etc. + + # I didn't check the other ones + # TODO: User can provide a "terminal-program" in the settings, say you can get inspired by Packages/CodePatcher/Server/linux-x64/terminal-run.sh + # the script is run with 2 args, a title and a command script to run. + +# If called with 0 args, signal the capability to start a terminal. +# If you add your own terminal, make sure to also return 0 when called with 0 args. + +TITLE="$1" +COMMAND="$2" + +if [ -z "$1" ]; then + [ -x "$(command -v gnome-terminal)" ] && exit 0 + [ -x "$(command -v xterm)" ] && exit 0 + [ -x "$(command -v konsole)" ] && exit 0 + [ -x "$(command -v terminator)" ] && exit 0 + [ -x "$(command -v urxvt)" ] && exit 0 + [ -x "$(command -v Alacritty)" ] && exit 0 + exit 1 +fi + +if [ -x "$(command -v gnome-terminal)" ]; then + gnome-terminal --title="$TITLE" -- "$SHELL" -c "$COMMAND" +elif [ -x "$(command -v xterm)" ]; then + xterm -title "$TITLE" -e "$SHELL -c '$COMMAND'" +elif [ -x "$(command -v konsole)" ]; then + konsole --title "$TITLE" --noclose -e "$SHELL -c '$COMMAND'" +elif [ -x "$(command -v terminator)" ]; then + terminator --title="$TITLE" --command="$SHELL -c '$COMMAND'" +elif [ -x "$(command -v urxvt)" ]; then + urxvt -title "$TITLE" -e "$SHELL" -c "clear && $COMMAND" +elif [ -x "$(command -v Alacritty)" ]; then + alacritty -t "$TITLE" -e "$SHELL -c '$COMMAND'" +fi diff --git a/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh.meta b/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh.meta new file mode 100644 index 0000000..cc05200 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: c3a7f96ba696eb649b581af931f26247 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/Third Party Notices.md b/Packages/com.singularitygroup.hotreload/Third Party Notices.md new file mode 100644 index 0000000..991b856 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Third Party Notices.md @@ -0,0 +1,327 @@ +# Dotnet - https://github.com/Microsoft/dotnet/blob/main/LICENSE + +The MIT License (MIT) + +Copyright (c) 2019 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +# Harmony2 - https://www.nuget.org/packages/Lib.Harmony/2.2.2/License + +MIT License + +Copyright (c) 2017 Andreas Pardeike + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +# Monomod - https://github.com/MonoMod/MonoMod/blob/master/LICENSE + +The MIT License (MIT) + +Copyright (c) 2015 - 2020 0x0ade + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +# Roslyn - https://github.com/dotnet/roslyn/blob/main/License.txt + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +# ZXing - https://github.com/zxing/zxing/blob/master/LICENSE + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +======================================================================== +jai-imageio +======================================================================== + +Copyright (c) 2005 Sun Microsystems, Inc. +Copyright © 2010-2014 University of Manchester +Copyright © 2010-2015 Stian Soiland-Reyes +Copyright © 2015 Peter Hull +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistribution of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistribution in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +Neither the name of Sun Microsystems, Inc. or the names of +contributors may be used to endorse or promote products derived +from this software without specific prior written permission. + +This software is provided "AS IS," without a warranty of any +kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND +WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY +EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL +NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF +USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS +DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR +ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND +REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR +INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +You acknowledge that this software is not designed or intended for +use in the design, construction, operation or maintenance of any +nuclear facility. + + +# Newtonsoft - https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +# Jetbrains.Annotations - https://github.com/JetBrains/java-annotations/blob/master/LICENSE.txt + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +Copyright 2000-2016 JetBrains s.r.o. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +# IL-repack - https://github.com/gluck/il-repack/blob/master/LICENSE + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + + +# Serilog - https://github.com/serilog/serilog/blob/dev/LICENSE + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + + +# SQLitePCLRaw - https://github.com/ericsink/SQLitePCL.raw/blob/master/LICENSE.TXT + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + + +# Humanizer - https://github.com/Humanizr/Humanizer/blob/main/LICENSE + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== + +Inflector (https://github.com/srkirkland/Inflector) +The MIT License (MIT) +Copyright (c) 2013 Scott Kirkland + +============================================================================== + +ByteSize (https://github.com/omar/ByteSize) +The MIT License (MIT) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) + +============================================================================== + + + +# ManagedEsent - https://github.com/microsoft/ManagedEsent/blob/master/LICENSE.md + +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# MsBuild Confuser - https://github.com/mkaring/ConfuserEx/blob/master/LICENSE.md + +The MIT License (MIT) + +Copyright (c) 2014 yck1509 +Copyright (c) 2018 Martin Karing + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +# Mono Cecil - https://github.com/jbevain/cecil/blob/master/LICENSE.txt + +The MIT License (MIT) + +Copyright (c) 2008 - 2015 Jb Evain +Copyright (c) 2008 - 2011 Novell, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# Semver - https://github.com/maxhauser/semver/blob/master/License.txt + +Copyright (c) 2013 Max Hauser, Jeff Walker + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Packages/com.singularitygroup.hotreload/Third Party Notices.md.meta b/Packages/com.singularitygroup.hotreload/Third Party Notices.md.meta new file mode 100644 index 0000000..0913d57 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Third Party Notices.md.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: b78a618995758fa44b4d046ff92ea93b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/Third Party Notices.md + uploadId: 870414 diff --git a/Packages/com.singularitygroup.hotreload/package.json b/Packages/com.singularitygroup.hotreload/package.json new file mode 100644 index 0000000..cae7096 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/package.json @@ -0,0 +1,20 @@ +{ + "displayName": "Hot Reload", + "name": "com.singularitygroup.hotreload", + "unity": "2019.4", + "description": "Change code and get immediate updates in the Unity Editor or on-device.", + "category": "Engineering", + "keywords": [ + "Hot", + "Reload", + "patch", + "code", + "sg", + "singularity" + ], + "version": "1.13.17", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } +} diff --git a/Packages/com.singularitygroup.hotreload/package.json.meta b/Packages/com.singularitygroup.hotreload/package.json.meta new file mode 100644 index 0000000..639ce0d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/package.json.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: b701518d055fa954b8adaa8000734e5a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.13.17 + assetPath: Packages/com.singularitygroup.hotreload/package.json + uploadId: 870414 diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 6de98f4..afd4471 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,5 +1,14 @@ { "dependencies": { + "com.singularitygroup.hotreload": { + "version": "file:com.singularitygroup.hotreload", + "depth": 0, + "source": "embedded", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, "com.unity.ai.navigation": { "version": "2.0.10", "depth": 0,