diff --git a/Assets/HotReload.meta b/Assets/HotReload.meta new file mode 100644 index 000000000..852c90e95 --- /dev/null +++ b/Assets/HotReload.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f19ed04311ab51409050a5842af58dd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/HotReload/Resources.meta b/Assets/HotReload/Resources.meta new file mode 100644 index 000000000..1e658fdbb --- /dev/null +++ b/Assets/HotReload/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 22b47c8a69ed0f548a9e8b8054c45ab6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/HotReload/Resources/HotReloadSettingsObject.asset b/Assets/HotReload/Resources/HotReloadSettingsObject.asset new file mode 100644 index 000000000..04013d3d1 --- /dev/null +++ b/Assets/HotReload/Resources/HotReloadSettingsObject.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 324c6fd3c103e0f418eb4b98c46bf63c, type: 3} + m_Name: HotReloadSettingsObject + m_EditorClassIdentifier: + IncludeInBuild: 1 + AllowAndroidAppToMakeHttpRequests: 0 + PromptsPrefab: {fileID: 4967086677379066170, guid: 0dc8d7047b14c44b7970c5d35665dbe1, type: 3} diff --git a/Assets/HotReload/Resources/HotReloadSettingsObject.asset.meta b/Assets/HotReload/Resources/HotReloadSettingsObject.asset.meta new file mode 100644 index 000000000..e430da7a9 --- /dev/null +++ b/Assets/HotReload/Resources/HotReloadSettingsObject.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a93d65fd647c6974ba29612c7d79186b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Documentation.meta b/Packages/com.singularitygroup.hotreload/Documentation.meta new file mode 100644 index 000000000..326840800 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Documentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a025eec5cd1851429c24e953a58d48b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf new file mode 100644 index 000000000..27968728e Binary files /dev/null and b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf differ diff --git a/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta new file mode 100644 index 000000000..754489fbd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 8999c2c2d9cadcb44a617a5df023bfa1 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 254358 + packageName: Hot Reload | Edit Code Without Compiling + packageVersion: 1.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Documentation/Documentation.pdf + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor.meta b/Packages/com.singularitygroup.hotreload/Editor.meta new file mode 100644 index 000000000..74ca4b032 --- /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 000000000..944cb6392 --- /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 000000000..980755a2f --- /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 000000000..ff59cda4b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/Attribution.cs + uploadId: 668105 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 000000000..cc065529c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs @@ -0,0 +1,68 @@ +using System; +using UnityEngine.Analytics; + +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; + } + } + } +} 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 000000000..c92c71584 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Attribution/VSAttribution.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CLI.meta b/Packages/com.singularitygroup.hotreload/Editor/CLI.meta new file mode 100644 index 000000000..7ce99ac50 --- /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 000000000..6c3833bcc --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs @@ -0,0 +1,128 @@ +using System.Diagnostics; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEngine; +using System; + +namespace SingularityGroup.HotReload.Editor.Cli { + internal static class CliUtils { + static readonly string projectIdentifier = GetProjectIdentifier(); + + class Config { + public bool singleInstance; + } + + public static string GetProjectIdentifier() { + if (File.Exists(PackageConst.ConfigFileName)) { + var config = JsonConvert.DeserializeObject(File.ReadAllText(PackageConst.ConfigFileName)); + if (config.singleInstance) { + return null; + } + } + var path = Path.GetDirectoryName(UnityHelper.DataPath); + var name = new DirectoryInfo(path).Name; + using (SHA256 sha256 = SHA256.Create()) { + byte[] inputBytes = Encoding.UTF8.GetBytes(path); + byte[] hashBytes = sha256.ComputeHash(inputBytes); + var hash = BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 6).ToUpper(); + return $"{name}-{hash}"; + } + } + + public static string GetTempDownloadFilePath(string osxFileName) { + if (UnityHelper.Platform == RuntimePlatform.OSXEditor) { + // project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app) + return Path.GetFullPath(PackageConst.LibraryCachePath + $"/HotReloadServerTemp/{osxFileName}"); + } else { + return Path.GetTempFileName(); + } + } + + public static string GetHotReloadTempDir() { + 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"); + } else { + if (projectIdentifier != null) { + return Path.Combine(Path.GetTempPath(), "HotReloadTemp", projectIdentifier); + } else { + return Path.Combine(Path.GetTempPath(), "HotReloadTemp"); + } + } + } + + 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) { + return Path.Combine(GetAppDataPath(), "asset-store", $"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 000000000..d52806cfc --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/CliUtils.cs + uploadId: 668105 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 000000000..660149030 --- /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 000000000..f6b068227 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/FallbackCliController.cs + uploadId: 668105 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 000000000..8cac04032 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs @@ -0,0 +1,239 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor; + +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() { + 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( + exposeServerToNetwork: HotReloadPrefs.ExposeServerToLocalNetwork, + allAssetChanges: HotReloadPrefs.AllAssetChanges, + createNoWindow: HotReloadPrefs.DisableConsoleWindow + ); + } + + internal static async Task StartAsync(bool exposeServerToNetwork, bool allAssetChanges, bool createNoWindow, LoginData loginData = null) { + var port = await Prepare().ConfigureAwait(false); + await ThreadUtility.SwitchToThreadPool(); + StartArgs args; + if (TryGetStartArgs(UnityHelper.DataPath, exposeServerToNetwork, allAssetChanges, createNoWindow, loginData, port, 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, LoginData loginData, int port, out StartArgs args) { + string serverDir; + if(!CliUtils.TryFindServerDir(out serverDir)) { + Log.Warning($"Failed to start the Hot Reload Server. " + + $"Unable to locate the 'Server' directory. " + + $"Make sure the 'Server' directory is " + + $"somewhere in the Assets folder inside a 'HotReload' folder or in the HotReload package"); + 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($"Failed to start the Hot Reload Server. Cannot find solution file. Please disable \"useBuiltInProjectGeneration\" in settings to enable custom project generation."); + args = null; + return false; + } + Log.Info("Using default project generation. If you encounter any problem with Unity's default project generation consider disabling it to use custom project generation."); + 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($"No .sln file found. Open any c# file to generate it so Hot Reload can work properly"); + } + + 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}"""; + if (loginData != null) { + cliArguments += $@" -email ""{loginData.email}"" -pass ""{loginData.password}"""; + } + 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; + RequestHelper.SetServerPort(port); + 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($"Preparing build info failed! On-device functionality might not work. Exception: {e}"); + didLogWarning = true; + } else { + Log.Debug($"Preparing build info failed! On-device functionality might not work. Exception: {e}"); + } + } + }); + } + + 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 000000000..c7339ed72 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/HotReloadCli.cs + uploadId: 668105 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 000000000..93d88262c --- /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 000000000..52c7def49 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/ICliController.cs + uploadId: 668105 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 000000000..9bb68ee0c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs @@ -0,0 +1,73 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +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("Could not start code patcher process."); + } + 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 000000000..5ba60e5e9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/LinuxCliController.cs + uploadId: 668105 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 000000000..d7034e581 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs @@ -0,0 +1,189 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +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 => false; + + /// 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("Failed to detect MacOS version, if Hot Reload fails to start, please contact support."); + 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 (UseCustomConsoleApp()) { + await StartCustomConsole(args, appExecutablePath); + } else { + await StartTerminal(args, cliExecutablePath); + } + } + + public Task StartCustomConsole(StartArgs args, string executablePath) { + process = Process.Start(new ProcessStartInfo { + // Path to the HotReload.app + FileName = executablePath, + Arguments = args.cliArguments, + UseShellExecute = false, + }); + 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("Failed to the run the start server command. ExitCode={0}\nFilepath: {1}", process.ExitCode, executableScriptPath); + } + } + else { + process.EnableRaisingEvents = true; + process.Exited += (_, __) => { + if (process.ExitCode != 0) { + Log.Warning("Failed to the run the start server command. ExitCode={0}\nFilepath: {1}", 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($"Expected to end with .zip, but it was: {zipPath}", nameof(zipPath)); + } + + if (!File.Exists(zipPath)) { + throw new ArgumentException($"zip file not found {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($"unzip failed with ExitCode {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("Failed to find .app directory and move it to " + 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 000000000..66c9330f1 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/OsxCliController.cs + uploadId: 668105 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 000000000..7d6b2a5c4 --- /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 000000000..33e4564d5 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/StartArgs.cs + uploadId: 668105 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 000000000..d623fda2a --- /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 000000000..e6b67d9ae --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CLI/WindowsCliController.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/CompileChecker.meta b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker.meta new file mode 100644 index 000000000..855fa44b8 --- /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 000000000..1ef81dd40 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs @@ -0,0 +1,71 @@ +#if UNITY_2019_1_OR_NEWER +using System; +using System.IO; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + class DefaultCompileChecker : ICompileChecker { + const string recompileFilePath = PackageConst.LibraryCachePath + "/recompile.txt"; + bool hasCompileErrors; + bool recompile; + public DefaultCompileChecker() { + 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("compile checker encountered issue: {0} {1}", 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; + } + } + } + + void OnCompilationFinished(object _) { + if(hasCompileErrors) { + //Don't recompile on compile errors. + hasCompileErrors = false; + } else { + 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 000000000..4d400d7a9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/DefaultCompileChecker.cs + uploadId: 668105 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 000000000..967e741b9 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs @@ -0,0 +1,17 @@ +using System; + +namespace SingularityGroup.HotReload.Editor { + interface ICompileChecker { + event Action onCompilationFinished; + } + + 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 000000000..7eb89f836 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/ICompileChecker.cs + uploadId: 668105 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 000000000..32569ab11 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs @@ -0,0 +1,54 @@ +#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 { + const string timestampFilePath = PackageConst.LibraryCachePath + "/lastCompileTimestamp.txt"; + 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 000000000..5e1869ad4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/CompileChecker/LegacyCompileChecker.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Constants.cs b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs new file mode 100644 index 000000000..b95d20bf8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Constants.cs @@ -0,0 +1,42 @@ + +namespace SingularityGroup.HotReload.Editor { + internal static class Constants { + public const string WebsiteURL = "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 = "https://hotreload.net/documentation/troubleshooting"; + public const string RecompileTroubleshootingURL = TroubleshootingURL + "#unity-recompiles-every-time-i-enterexit-playmode"; + public const string FeaturesDocumentationURL = DocumentationURL + "/features"; + public const string MultipleEditorsURL = DocumentationURL + "/multiple-editors"; + public const string VoteForAwardURL = "https://awards.unity.com/#best-development-tool"; + public const string UnityStoreRateAppURL = "https://assetstore.unity.com/packages/slug/254358#reviews"; + public const string ChangelogURL = WebsiteURL + "/changelog"; + 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 const string Only40EntriesShown = "Only last 40 entries are shown"; + } +} \ 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 000000000..cfaedfd94 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Constants.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Demo.meta b/Packages/com.singularitygroup.hotreload/Editor/Demo.meta new file mode 100644 index 000000000..1c88280a9 --- /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 000000000..895c68506 --- /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 000000000..cb96ab3a0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Demo/EditorDemo.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs new file mode 100644 index 000000000..584c826ea --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs @@ -0,0 +1,912 @@ +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 SingularityGroup.HotReload.RuntimeDependencies; +using UnityEditor; +using UnityEngine; +using Debug = UnityEngine.Debug; +using Task = System.Threading.Tasks.Task; +using System.Reflection; +using System.Runtime.CompilerServices; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor.Compilation; + +[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 { + const string sessionFilePath = PackageConst.LibraryCachePath + "/sessionId.txt"; + const string patchesFilePath = PackageConst.LibraryCachePath + "/patches.json"; + + internal static readonly ServerDownloader serverDownloader; + internal static bool _compileError; + internal static bool _applyingFailed; + internal static bool _appliedPartially; + + 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 OnPatchHandled; + + + internal static Config config; + + static bool quitting; + static EditorCodePatcher() { + if(init) { + //Avoid infinite recursion in case the static constructor gets accessed via `InitPatchesBlocked` below + return; + } + if (File.Exists(PackageConst.ConfigFileName)) { + config = JsonConvert.DeserializeObject(File.ReadAllText(PackageConst.ConfigFileName)); + } 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(); + timer = new Timer(OnIntervalThreaded, (Action) OnIntervalMainThread, 500, 500); + + UpdateHost(); + licenseType = UnityLicenseHelper.GetLicenseType(); + var 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(); + }; + + CompilationPipeline.compilationFinished += obj => { + // 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(); + }; + DetectEditorStart(); + DetectVersionUpdate(); + SingularityGroup.HotReload.Demo.Demo.I = new EditorDemo(); + RecordActiveDaysForRateApp(); + if (EditorApplication.isPlayingOrWillChangePlaymode) { + CodePatcher.I.InitPatchesBlocked(patchesFilePath); + HotReloadTimelineHelper.InitPersistedEvents(); + } + +#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; + } + } + }; + } + + public static void ResetSettingsOnQuit() { + quitting = true; + 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 (!HotReloadPrefs.AutoRecompileUnsupportedChanges + || HotReloadTimelineHelper.UnsupportedChangesCount == 0 + && (!HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges || HotReloadTimelineHelper.PartiallySupportedChangesCount == 0) + || _compileError + || EditorApplication.isPlaying && !HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode + ) { + return false; + } + + if (HotReloadPrefs.ShowCompilingUnsupportedNotifications) { + EditorWindowHelper.ShowNotification(EditorWindowHelper.NotificationStatus.NeedsRecompile); + } + HotReloadRunTab.Recompile(); + return true; + } + + 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() { + // 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() { + 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)) { + return; + } + ServerHealthCheck.instance.CheckHealth(); + if (!ServerHealthCheck.I.IsServerHealthy) { + return; + } + var restartServer = EditorUtility.DisplayDialog("Hot Reload", + $"When updating Hot Reload, the server must be restarted for the update to take effect." + + "\nDo you want to restart it now?", + "Restart server", "Don't restart"); + if (restartServer) { + RestartCodePatcher().Forget(); + } + } + + private static void UpdateHost() { + RequestHelper.SetServerInfo(new PatchServerInfo(RequestHelper.defaultServerHost, HotReloadState.ServerPort, null, Path.GetFullPath("."))); + } + + static void OnIntervalThreaded(object o) { + ServerHealthCheck.instance.CheckHealth(); + 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; + } + + static async Task FlushErrors() { + var response = await RequestHelper.RequestFlushErrors(); + if (response == null) { + return; + } + foreach (var responseWarning in response.warnings) { + if (responseWarning.Contains("Scripts have compile errors")) { + Log.Error(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; + 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) { + // 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 (!ServerHealthCheck.I.IsServerHealthy) { + stopping = false; + } + if (startupProgress?.Item1 == 1) { + starting = false; + } + if (!_requestingFlushErrors && Running) { + RequestFlushErrors().Forget(); + } + CheckEditorSettings(); + } + + static void CheckEditorSettings() { + if (quitting) { + return; + } + CheckAutoRefresh(); + CheckScriptCompilation(); + CheckPlaymodeTint(); + CheckAssetDatabaseRefresh(); + } + + static void CheckAutoRefresh() { + if (HotReloadPrefs.AllowDisableUnityAutoRefresh && ServerHealthCheck.I.IsServerHealthy) { + AutoRefreshSettingChecker.Apply(); + AutoRefreshSettingChecker.Check(); + } else { + AutoRefreshSettingChecker.Reset(); + } + } + + static void CheckScriptCompilation() { + if (HotReloadPrefs.AllowDisableUnityAutoRefresh && ServerHealthCheck.I.IsServerHealthy) { + ScriptCompilationSettingChecker.Apply(); + ScriptCompilationSettingChecker.Check(); + } else { + ScriptCompilationSettingChecker.Reset(); + } + } + + static string[] assetExtensionBlacklist = new[] { + ".cs", + // TODO 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", + // ".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) { + // ignore directories + if (Directory.Exists(assetPath)) { + return; + } + foreach (var compileFile in compileFiles) { + if (assetPath.EndsWith(compileFile, StringComparison.Ordinal)) { + HotReloadTimelineHelper.CreateErrorEventEntry($"errors: AssemblyFileEdit: Editing assembly files requires recompiling in Unity. in {assetPath}", entryType: EntryType.Foldout); + _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($"errors: NativePluginEdit: Editing native plugins requires recompiling in Unity. in {assetPath}", entryType: EntryType.Foldout); + _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 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) + && relativePathPackages.StartsWith("..", StringComparison.Ordinal) + ) { + return; + } + try { + if (!File.Exists(assetPath)) { + AssetDatabase.DeleteAsset(relativePath); + } else { + AssetDatabase.ImportAsset(relativePath, ImportAssetOptions.ForceUpdate); + } + } catch (Exception e){ + Log.Warning($"Refreshing asset at path: {assetPath} failed due to exception: {e}"); + } + } + + 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 (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) { + HandleRemovedUnityMethods(response.removedMethod); + + RegisterPatchesResult patchResult = null; + if (response.patches?.Length > 0) { + LogBurstHint(response); + patchResult = CodePatcher.I.RegisterPatches(response, persist: true); + CodePatcher.I.SaveAppliedPatches(patchesFilePath).Forget(); + } + + var partiallySupportedChangesFiltered = new List(response.partiallySupportedChanges ?? Array.Empty()); + partiallySupportedChangesFiltered.RemoveAll(x => !HotReloadTimelineHelper.GetPartiallySupportedChangePref(x)); + var failuresDeduplicated = new HashSet(response.failures ?? Array.Empty()); + _compileError = response.failures?.Any(failure => failure.Contains("error CS")) ?? false; + _applyingFailed = response.failures?.Length > 0 || patchResult?.patchFailures.Count > 0; + _appliedPartially = !_applyingFailed && partiallySupportedChangesFiltered.Count > 0; + + if (_compileError) { + HotReloadTimelineHelper.EventsTimeline.RemoveAll(e => e.alertType == AlertType.CompileError); + foreach (var failure in failuresDeduplicated) { + if (failure.Contains("error CS")) { + HotReloadTimelineHelper.CreateErrorEventEntry(failure); + } + } + } 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); + } + } + HotReloadTimelineHelper.CreateReloadFinishedWithWarningsEventEntry(); + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.UnsupportedChanges); + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + TryRecompileUnsupportedChanges(); + } + } else if (_appliedPartially) { + foreach (var responsePartiallySupportedChange in partiallySupportedChangesFiltered) { + HotReloadTimelineHelper.CreatePartiallyAppliedEventEntry(responsePartiallySupportedChange, entryType: EntryType.Child, detailed: false); + } + HotReloadTimelineHelper.CreateReloadPartiallyAppliedEventEntry(); + + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately || UnityEditorInternal.InternalEditorUtility.isApplicationActive) { + TryRecompileUnsupportedChanges(); + } + } else { + HotReloadTimelineHelper.CreateReloadFinishedEventEntry(); + } + + // 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); + } + + if (HotReloadWindow.Current) { + HotReloadWindow.Current.Repaint(); + } + HotReloadState.LastPatchId = response.id; + OnPatchHandled?.Invoke(); + } + + static string GetMethodName(SMethod method) { + var spaceIndex = method.displayName.IndexOf(" ", StringComparison.Ordinal); + if (spaceIndex > 0) { + return method.displayName.Substring(spaceIndex); + } + return method.displayName; + } + + + static void HandleRemovedUnityMethods(SMethod[] removedMethods) { + if (removedMethods == null) { + return; + } + foreach(var sMethod in removedMethods) { + try { + var candidates = CodePatcher.I.SymbolResolver.Resolve(sMethod.assemblyName.Replace(".dll", "")); + var asm = candidates[0]; + var module = asm.GetLoadedModules()[0]; + var oldMethod = module.ResolveMethod(sMethod.metadataToken); + UnityEventHelper.RemoveUnityEventMethod(oldMethod); + } catch(Exception ex) { + Log.Warning("Encountered exception in RemoveUnityEventMethod: {0} {1}", ex.GetType().Name, ex.Message); + } + } + } + + [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("A unity job was hot reloaded. " + + "This will cause a harmless warning that can be ignored. " + + $"More info about this can be found here: {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(); + } + Task.Run(() => File.Delete(patchesFilePath)); + HotReloadTimelineHelper.ClearPersistance(); + } + + static bool requestingCompile; + static async Task RequestCompile() { + requestingCompile = true; + try { + await RequestHelper.RequestClearPatches(); + await ProjectGeneration.ProjectGeneration.GenerateSlnAndCsprojFiles(Application.dataPath); + await RequestHelper.RequestCompile(); + } 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; + CodePatcher.I.ClearPatchedMethods(); + try { + requestingStart = true; + startupProgress = Tuple.Create(0f, "Starting Hot Reload"); + serverStartedAt = DateTime.UtcNow; + await HotReloadCli.StartAsync(exposeToNetwork, allAssetChanges, disableConsoleWindow, loginData).ConfigureAwait(false); + } + catch (Exception ex) { + ThreadUtility.LogException(ex); + } + finally { + requestingStart = false; + } + } + + private static bool requestingStop; + internal static async Task StopCodePatcher() { + stopping = true; + starting = false; + if (requestingStop) { + return; + } + CodePatcher.I.ClearPatchedMethods(); + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning); + try { + requestingStop = true; + await HotReloadCli.StopAsync().ConfigureAwait(false); + serverStoppedAt = DateTime.UtcNow; + await ThreadUtility.SwitchToMainThread(); + 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 async Task DownloadAndRun(LoginData loginData = null) { + if (requestingDownloadAndRun) { + return false; + } + stopping = false; + requestingDownloadAndRun = true; + try { + if (DownloadRequired) { + var ok = await serverDownloader.PromptForDownload(); + if (!ok) { + return false; + } + } + await StartCodePatcher(loginData); + return true; + } finally { + requestingDownloadAndRun = false; + } + } + + 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) { + Attribution.RegisterLogin(resp); + + bool consumptionsChanged = Status?.freeSessionRunning != resp.freeSessionRunning || Status?.freeSessionEndTime != resp.freeSessionEndTime; + bool expiresAtChanged = Status?.licenseExpiresAt != resp.licenseExpiresAt; + if (resp.consumptionsUnavailableReason == ConsumptionsUnavailableReason.UnrecoverableError + && Status?.consumptionsUnavailableReason != ConsumptionsUnavailableReason.UnrecoverableError + ) { + Log.Error("Free charges unavailabe. Please contact support if the issue persists."); + } + 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) ? "Starting Hot Reload" : 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) { + 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; + } + } +} 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 000000000..69ca19a99 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/EditorCodePatcher.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs new file mode 100644 index 000000000..7a2dd4684 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SingularityGroup.HotReload.DTO; + +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, + } + + 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 }, + // green icon: + { IndicationStatus.Started, greenIconPath }, + // log icons: + { IndicationStatus.Reloaded, HotReloadTimelineHelper.alertIconString[AlertType.AppliedChange] }, + { IndicationStatus.Unsupported, 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 readonly Dictionary IndicationText = new Dictionary { + { IndicationStatus.FinishRegistration, "Finish Registration" }, + { IndicationStatus.Started, "Waiting for code changes" }, + { IndicationStatus.Stopping, "Stopping Hot Reload" }, + { IndicationStatus.Stopped, "Hot Reload inactive" }, + { IndicationStatus.Installing, "Installing" }, + { IndicationStatus.Starting, "Starting Hot Reload" }, + { IndicationStatus.Reloaded, "Reload finished" }, + { IndicationStatus.PartiallySupported, "Changes partially applied" }, + { IndicationStatus.Unsupported, "Finished with warnings" }, + { IndicationStatus.Patching, "Reloading" }, + { IndicationStatus.Compiling, "Compiling" }, + { IndicationStatus.CompileErrors, "Scripts have compile errors" }, + { IndicationStatus.ActivationFailed, "Activation failed" }, + { IndicationStatus.Loading, "Loading" }, + }; + + 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 (!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; + } + 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; + } + 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($"Indication text not found for status {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 000000000..692711511 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/EditorIndicationState.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs b/Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs new file mode 100644 index 000000000..875381dd3 --- /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 000000000..ff77c6400 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/GitUtil.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Helpers.meta b/Packages/com.singularitygroup.hotreload/Editor/Helpers.meta new file mode 100644 index 000000000..9a003a66d --- /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 000000000..89cc54507 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using UnityEditor; +using System.Linq; +using System.Runtime.CompilerServices; +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("To compile C# files same as a Player build, we must omit projects which aren't part of the selected Player build."); + var omitted = GetOmittedProjects(EditorUserBuildSettings.activeScriptCompilationDefines); + Log.Info("---------"); + + foreach (var name in omitted) { + Log.Info("omitted editor/other project named: {0}", 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("found project named {0}", name); + } + foreach (var playerAssemblyName in playerAssemblies) { + Log.Debug("player assembly named {0}", 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 000000000..163c22c0b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/AssemblyOmission.cs + uploadId: 668105 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 000000000..d0f9130fb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs @@ -0,0 +1,144 @@ +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 BuildInfoInput(string allDefineSymbols, BuildTarget activeBuildTarget, string[] omittedProjects, bool batchMode) { + this.allDefineSymbols = allDefineSymbols; + this.activeBuildTarget = activeBuildTarget; + this.omittedProjects = omittedProjects; + this.batchMode = batchMode; + } + } + + 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); + + return new BuildInfoInput( + allDefineSymbols: allDefineSymbols, + activeBuildTarget: buildTarget, + omittedProjects: omittedProjects, + batchMode: batchMode + ); + } + + 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 + )); + } + + 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, + }; + } + + 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. + public 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 000000000..16b85c188 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/BuildInfoHelper.cs + uploadId: 668105 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 000000000..92553a398 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs @@ -0,0 +1,101 @@ +using System; +using System.Text.RegularExpressions; +using UnityEngine; +using System.Threading.Tasks; +using UnityEditor; +using System.Collections.Generic; +using System.Linq; + +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("[Hot Reload] Applying patches...")}, + { NotificationStatus.NeedsRecompile, new GUIContent("[Hot Reload] Unsupported Changes detected! Recompiling...")}, + }; + + 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 000000000..be779f6c1 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/EditorWindowHelper.cs + uploadId: 668105 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 000000000..fab85d7cf --- /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 000000000..5e11e7ba0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/GUIHelper.cs + uploadId: 668105 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 000000000..d4a1358a2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEditor.PackageManager; +using UnityEditor.PackageManager.Requests; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + + public enum HotReloadSuggestionKind { + UnsupportedChanges, + UnsupportedPackages, + [Obsolete] SymbolicLinks, + AutoRecompiledWhenPlaymodeStateChanges, + UnityBestDevelopmentToolAward2023, +#if UNITY_2022_1_OR_NEWER + AutoRecompiledWhenPlaymodeStateChanges2022, +#endif + MultidimensionalArrays, + EditorsWithoutHRRunning, + } + + internal static class HotReloadSuggestionsHelper { + internal static void SetSuggestionsShown(HotReloadSuggestionKind hotReloadSuggestionKind) { + 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}"); + } + + // used for cases where suggestion might need to be shown more than once + internal static void SetSuggestionActive(HotReloadSuggestionKind hotReloadSuggestionKind) { + if (EditorPrefs.GetBool($"HotReloadWindow.SuggestionsShown.{hotReloadSuggestionKind}")) { + return; + } + EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", true); + + AlertEntry entry; + if (suggestionMap.TryGetValue(hotReloadSuggestionKind, out entry) && !HotReloadTimelineHelper.Suggestions.Contains(entry)) { + HotReloadTimelineHelper.Suggestions.Insert(0, entry); + HotReloadState.ShowingRedDot = true; + } + } + + internal static void SetSuggestionInactive(HotReloadSuggestionKind hotReloadSuggestionKind) { + EditorPrefs.SetBool($"HotReloadWindow.SuggestionsActive.{hotReloadSuggestionKind}", false); + AlertEntry entry; + if (suggestionMap.TryGetValue(hotReloadSuggestionKind, out entry)) { + HotReloadTimelineHelper.Suggestions.Remove(entry); + } + } + + internal 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("Documentation", Constants.RecompileTroubleshootingURL); + internal static readonly OpenURLButton featuresDocumentationButton = new OpenURLButton("Documentation", Constants.FeaturesDocumentationURL); + internal static readonly OpenURLButton multipleEditorsDocumentationButton = new OpenURLButton("Documentation", Constants.MultipleEditorsURL); + public static Dictionary suggestionMap = new Dictionary { + { HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023, new AlertEntry( + AlertType.Suggestion, + "Vote for the \"Best Development Tool\" Award!", + "Hot Reload was nominated for the \"Best Development Tool\" Award. Please consider voting. Thank you!", + actionData: () => { + GUILayout.Space(6f); + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(" Vote ")) { + Application.OpenURL(Constants.VoteForAwardURL); + SetSuggestionInactive(HotReloadSuggestionKind.UnityBestDevelopmentToolAward2023); + } + GUILayout.FlexibleSpace(); + } + }, + timestamp: DateTime.Now, + entryType: EntryType.Foldout + )}, + { HotReloadSuggestionKind.UnsupportedChanges, new AlertEntry( + AlertType.Suggestion, + "Which changes does Hot Reload support?", + "Hot Reload supports most code changes, but there are some limitations. Generally, changes to the method definition and body are allowed. Non-method changes (like adding/editing classes and fields) are not supported. See the documentation for the list of current features and our current roadmap", + actionData: () => { + GUILayout.Space(10f); + using (new EditorGUILayout.HorizontalScope()) { + featuresDocumentationButton.OnGUI(); + GUILayout.FlexibleSpace(); + } + }, + timestamp: DateTime.Now, + entryType: EntryType.Foldout + )}, + { HotReloadSuggestionKind.UnsupportedPackages, new AlertEntry( + AlertType.Suggestion, + "Unsupported package detected", + "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 work. Contact us if these packages are a big part of your project", + 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, + "Unity recompiles on enter/exit play mode?", + "If you have an issue with the Unity Editor recompiling when the Play Mode state changes, please consult the documentation, and don’t hesitate to reach out to us if you need assistance", + 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, + "Unsupported setting detected", + "The 'Sprite Packer Mode' setting can cause unintended recompilations if set to 'Sprite Atlas V1 - Always Enabled'", + iconType: AlertType.UnsupportedChange, + actionData: () => { + GUILayout.Space(10f); + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(" Use \"Sprite Atlas V2\" ")) { + EditorSettings.spritePackerMode = SpritePackerMode.SpriteAtlasV2; + } + if (GUILayout.Button(" Open Settings ")) { + SettingsService.OpenProjectSettings("Project/Editor"); + } + if (GUILayout.Button(" Ignore suggestion ")) { + SetSuggestionInactive(HotReloadSuggestionKind.AutoRecompiledWhenPlaymodeStateChanges2022); + } + + GUILayout.FlexibleSpace(); + } + }, + timestamp: DateTime.Now, + entryType: EntryType.Foldout, + hasExitButton: false + )}, +#endif + { HotReloadSuggestionKind.MultidimensionalArrays, new AlertEntry( + AlertType.Suggestion, + "Use jagged instead of multidimensional arrays", + "Hot Reload doesn't support multidimensional ([,]) arrays. Jagged arrays ([][]) are a better alternative, and Microsoft recommends using them instead", + iconType: AlertType.UnsupportedChange, + actionData: () => { + GUILayout.Space(10f); + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(" Learn more ")) { + Application.OpenURL("https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1814"); + } + GUILayout.FlexibleSpace(); + } + }, + timestamp: DateTime.Now, + entryType: EntryType.Foldout + )}, + { HotReloadSuggestionKind.EditorsWithoutHRRunning, new AlertEntry( + AlertType.Suggestion, + "Some Unity instances don't have Hot Reload running.", + "Make sure that either: \n1) Hot Reload is installed and running on all Editor instances, or \n2) Hot Reload is stopped in all Editor instances where it is installed.", + actionData: () => { + GUILayout.Space(10f); + using (new EditorGUILayout.HorizontalScope()) { + if (GUILayout.Button(" Stop Hot Reload ")) { + EditorCodePatcher.StopCodePatcher().Forget(); + } + GUILayout.Space(5f); + + multipleEditorsDocumentationButton.OnGUI(); + GUILayout.Space(5f); + + if (GUILayout.Button(" Don't show again ")) { + HotReloadSuggestionsHelper.SetSuggestionsShown(HotReloadSuggestionKind.EditorsWithoutHRRunning); + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning); + } + GUILayout.FlexibleSpace(); + GUILayout.FlexibleSpace(); + } + }, + timestamp: DateTime.Now, + entryType: EntryType.Foldout, + iconType: AlertType.UnsupportedChange + )}, + }; + + static ListRequest listRequest; + static string[] unsupportedPackages = new[] { + "com.unity.entities", + "com.firstgeargames.fishnet", + }; + static List unsupportedPackagesList; + static DateTime lastPlaymodeChange; + + public static void Init() { + 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 (listRequest.IsCompleted && + unsupportedPackagesList == null) + { + unsupportedPackagesList = new List(); + var packages = listRequest.Result; + foreach (var packageInfo in packages) { + 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) { + 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 showSuggestion = await Task.Run(() => { + var runningUnities = Process.GetProcessesByName("Unity").Length; + var runningPatchers = Process.GetProcessesByName("CodePatcherCLI").Length; + return runningUnities > runningPatchers; + }); + if (!showSuggestion) { + HotReloadSuggestionsHelper.SetSuggestionInactive(HotReloadSuggestionKind.EditorsWithoutHRRunning); + return; + } + if (!HotReloadState.ShowedEditorsWithoutHR && ServerHealthCheck.I.IsServerHealthy) { + HotReloadSuggestionsHelper.SetSuggestionActive(HotReloadSuggestionKind.EditorsWithoutHRRunning); + HotReloadState.ShowedEditorsWithoutHR = true; + } + } finally { + checkingEditorsWihtoutHR = false; + lastCheckedUnityInstances = DateTime.UtcNow; + } + } + } +} 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 000000000..92a2702b6 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadSuggestionsHelper.cs + uploadId: 668105 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 000000000..a353ffd6f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using JetBrains.Annotations; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + + +namespace SingularityGroup.HotReload.Editor { + internal enum TimelineType { + Suggestions, + Timeline, + } + + internal enum AlertType { + Suggestion, + UnsupportedChange, + CompileError, + PartiallySupportedChange, + AppliedChange + } + + internal enum AlertEntryType { + Error, + Failure, + PatchApplied, + PartiallySupportedChange, + } + + 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 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)) { + this.alertEntryType = alertEntryType; + this.createdAt = createdAt; + this.detiled = detiled; + this.entryType = entryType; + this.errorString = errorString; + this.methodName = methodName; + this.methodSimpleName = methodSimpleName; + this.partiallySupportedChange = partiallySupportedChange; + } + } + + 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 void InitPersistedEvents() { + if (!File.Exists(filePath)) { + return; + } + var redDotShown = HotReloadState.ShowingRedDot; + try { + var persistedAlertData = 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; + case AlertEntryType.Failure: + if (alertData.entryType == EntryType.Parent) { + CreateReloadFinishedWithWarningsEventEntry(createdAt: alertData.createdAt); + } 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); + break; + case AlertEntryType.PartiallySupportedChange: + if (alertData.entryType == EntryType.Parent) { + CreateReloadPartiallyAppliedEventEntry(createdAt: alertData.createdAt); + } else { + CreatePartiallyAppliedEventEntry(alertData.partiallySupportedChange, entryType: alertData.entryType, detailed: alertData.detiled, createdAt: alertData.createdAt); + } + break; + } + } + } catch (Exception e) { + Log.Warning($"Failed initializing Hot Reload event entries on start: {e}"); + } finally { + // Ensure red dot is not triggered for existing entries + HotReloadState.ShowingRedDot = redDotShown; + } + } + + internal static void PersistTimeline() { + var alertDatas = new AlertData[eventsTimeline.Count]; + for (var i = 0; i < eventsTimeline.Count; i++) { + alertDatas[i] = eventsTimeline[i].alertData; + } + var persistedData = new PersistedAlertData(alertDatas); + try { + File.WriteAllText(path: filePath, contents: JsonConvert.SerializeObject(persistedData)); + } catch (Exception e) { + Log.Warning($"Failed persisting Hot Reload event entries: {e}"); + } + } + + internal static void ClearPersistance() { + try { + File.Delete(filePath); + } catch { + // ignore + } + 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" }, + }; + + public static Dictionary partiallySupportedChangeDescriptions = new Dictionary { + {PartiallySupportedChange.LambdaClosure, "A lambda closure was edited (captured variable was added or removed). Changes to it will only be visible to the next created lambda(s)."}, + {PartiallySupportedChange.EditAsyncMethod, "An async method was edited. Changes to it will only be visible the next time this method is called."}, + {PartiallySupportedChange.AddMonobehaviourMethod, "A new method was added. It will not show up in the Inspector until the next full recompilation."}, + {PartiallySupportedChange.EditMonobehaviourField, "A field in a MonoBehaviour was removed or reordered. The inspector will not notice this change until the next full recompilation."}, + {PartiallySupportedChange.EditCoroutine, "An IEnumerator/IEnumerable was edited. When used as a coroutine, changes to it will only be visible the next time the coroutine is created."}, + {PartiallySupportedChange.AddEnumMember, "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."}, + {PartiallySupportedChange.EditFieldInitializer, "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."}, + {PartiallySupportedChange.AddMethodWithAttributes, "A method with attributes was added. Method attributes will not have any effect until the next full recompilation."}, + }; + + 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 CompileErrorsCount => EventsTimeline.Count(alert => alert.alertType == AlertType.CompileError); + internal static int AppliedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.AppliedChange); + + static Regex shortDescriptionRegex = new Regex(@"^(\w+)\s(\w+)(?=:)", RegexOptions.Compiled); + + internal static int GetRunTabTimelineEventCount() { + int total = 0; + if (HotReloadPrefs.RunTabUnsupportedChangesFilter) { + total += UnsupportedChangesCount; + } + if (HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter) { + total += PartiallySupportedChangesCount; + } + 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("Recompile", 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 = 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") + ? "Compile error" + : "Unsupported change"; + 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) + )); + } + + 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 = $"Failed applying patch to method"; + Action actionData = () => RenderErrorEventActions(errorData.error, errorData); + InsertEntry(new AlertEntry( + timestamp: timestamp, + alertType : AlertType.UnsupportedChange, + title: title, + description: $"{title}: {methodName}, tap here to see more.", + shortDescription: methodSimpleName, + actionData: actionData, + entryType: entryType, + alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, errorString: errorString, methodName: methodName, methodSimpleName: methodSimpleName, entryType: entryType) + )); + } + + internal static void CreateReloadFinishedEventEntry(DateTime? createdAt = null) { + var timestamp = createdAt ?? DateTime.Now; + InsertEntry(new AlertEntry( + timestamp: timestamp, + alertType : AlertType.AppliedChange, + title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Reloaded], + description: "No issues found", + entryType: EntryType.Standalone, + alertData: new AlertData(AlertEntryType.PatchApplied, createdAt: timestamp, entryType: EntryType.Standalone) + )); + } + + internal static void CreateReloadFinishedWithWarningsEventEntry(DateTime? createdAt = null) { + var timestamp = createdAt ?? DateTime.Now; + InsertEntry(new AlertEntry( + timestamp: timestamp, + alertType : AlertType.UnsupportedChange, + title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Unsupported], + description: "See detailed entries below", + entryType: EntryType.Parent, + alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, entryType: EntryType.Parent) + )); + } + + internal static void CreateReloadPartiallyAppliedEventEntry(DateTime? createdAt = null) { + var timestamp = createdAt ?? DateTime.Now; + InsertEntry(new AlertEntry( + timestamp: timestamp, + alertType : AlertType.PartiallySupportedChange, + title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.PartiallySupported], + description: "See detailed entries below", + entryType: EntryType.Parent, + alertData: new AlertData(AlertEntryType.PartiallySupportedChange, 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 ? "Change partially applied" : 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("Ignore this event type ", 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) { + 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.AddEnumMember: + return nameof(PartiallySupportedChange.AddEnumMember); + case PartiallySupportedChange.EditFieldInitializer: + return nameof(PartiallySupportedChange.EditFieldInitializer); + case PartiallySupportedChange.AddMethodWithAttributes: + return nameof(PartiallySupportedChange.AddMethodWithAttributes); + 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 000000000..9b33c8a80 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/HotReloadTimelineHelper.cs + uploadId: 668105 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 000000000..5755fdba0 --- /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 000000000..c67a669cb --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/Spinner.cs + uploadId: 668105 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 000000000..b158a6777 --- /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 000000000..8f137cfd4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Helpers/UnitySettingsHelper.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs new file mode 100644 index 000000000..5a85540f6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs @@ -0,0 +1,95 @@ +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; + + if (HotReloadWindowStyles.windowScreenWidth <= Constants.ConsumptionsHideWidth + || HotReloadWindowStyles.windowScreenHeight <= Constants.ConsumptionsHideHeight + || source == PopupSource.Overlay + ) { + HotReloadRunTab.RenderLicenseInfo(currentState); + } + + 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 000000000..baf11c017 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadEventPopup.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs new file mode 100644 index 000000000..19202fe3d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs @@ -0,0 +1,178 @@ +#if UNITY_2021_2_OR_NEWER +using System; +using System.Collections.Generic; +using UnityEditor.Overlays; +using UnityEngine.UIElements; +using UnityEditor; +using UnityEngine; +using UnityEditor.Toolbars; + +namespace SingularityGroup.HotReload.Editor { + [Overlay(typeof(SceneView), "Hot Reload", true)] + [Icon("Assets/HotReload/Editor/Resources/Icon_DarkMode.png")] + internal class HotReloadOverlay : ToolbarOverlay { + HotReloadOverlay() : base(HotReloadToolbarIndicationButton.id, HotReloadToolbarEventsButton.id, HotReloadToolbarRecompileButton.id) { + 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 = "Events"; + 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 = "Recompile"; + 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 = "Hot Reload Indication" }; + 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 000000000..7c271ec65 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadOverlay.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs new file mode 100644 index 000000000..eece5ab8b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs @@ -0,0 +1,429 @@ +using System; +using System.Globalization; +using System.IO; +using JetBrains.Annotations; +using SingularityGroup.HotReload.Editor.Cli; +using UnityEditor; +using UnityEngine; + +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 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 AutoRecompileUnsupportedChangesKey = "HotReloadWindow.AutoRecompileUnsupportedChanges"; + private const string AutoRecompilePartiallyUnsupportedChangesKey = "HotReloadWindow.AutoRecompilePartiallyUnsupportedChanges"; + private const string ShowNotificationsKey = "HotReloadWindow.ShowNotifications"; + private const string ShowPatchingNotificationsKey = "HotReloadWindow.ShowPatchingNotifications"; + private const string ShowCompilingUnsupportedNotificationsKey = "HotReloadWindow.ShowCompilingUnsupportedNotifications"; + private const string AutoRecompileUnsupportedChangesImmediatelyKey = "HotReloadWindow.AutoRecompileUnsupportedChangesImmediately"; + private const string AutoRecompileUnsupportedChangesOnExitPlayModeKey = "HotReloadWindow.AutoRecompileUnsupportedChangesOnExitPlayMode"; + private const string AutoRecompileUnsupportedChangesInPlayModeKey = "HotReloadWindow.AutoRecompileUnsupportedChangesInPlayMode"; + 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 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 RunTabAppliedPatchesFilterKey = "HotReloadWindow.RunTabAppliedPatchesFilter"; + private const string RecompileDialogueShownKey = "HotReloadWindow.RecompileDialogueShown"; + private const string OpenedWindowAtLeastOnceKey = "HotReloadWindow.OpenedWindowAtLeastOnce"; + + public const string DontShowPromptForDownloadKey = "ServerDownloader.DontShowPromptForDownload"; + + [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 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 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 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 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 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])); + } + } +} 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 000000000..1346eed8e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadPrefs.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs new file mode 100644 index 000000000..fdb536d31 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs @@ -0,0 +1,70 @@ +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) { + 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 000000000..f40133eb1 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadSettingsEditor.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs new file mode 100644 index 000000000..a087f34a4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs @@ -0,0 +1,37 @@ +using UnityEditor; + +namespace SingularityGroup.HotReload.Editor { + internal static class HotReloadState { + private const string ServerPortKey = "HotReloadWindow.ServerPort"; + private const string LastPatchIdKey = "HotReloadWindow.LastPatchId"; + private const string ShowingRedDotKey = "HotReloadWindow.ShowingRedDot"; + private const string ShowedEditorsWithoutHRKey = "HotReloadWindow.ShowedEditorWithoutHR"; + private const string RecompiledUnsupportedChangesOnExitPlaymodeKey = "HotReloadWindow.RecompiledUnsupportedChangesOnExitPlaymode"; + + public static int ServerPort { + get { return SessionState.GetInt(ServerPortKey, RequestHelper.defaultPort); } + set { SessionState.SetInt(ServerPortKey, value); } + } + + public static string LastPatchId { + get { return SessionState.GetString(LastPatchIdKey, string.Empty); } + 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 RecompiledUnsupportedChangesOnExitPlaymode { + get { return SessionState.GetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, false); } + set { SessionState.SetBool(RecompiledUnsupportedChangesOnExitPlaymodeKey, 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 000000000..1566fd5a5 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/HotReloadState.cs + uploadId: 668105 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 000000000..09e5105fc 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 000000000..de3c64f9d --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Icon_Player.png + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs b/Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs new file mode 100644 index 000000000..330fbc194 --- /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 000000000..f3dd7f04f --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/InspectorFreezeFix.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Installation.meta b/Packages/com.singularitygroup.hotreload/Editor/Installation.meta new file mode 100644 index 000000000..09a0fe38b --- /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 000000000..2575d7bd1 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Editor.Cli; + +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) { + if (PackageConst.IsAssetStoreBuild) { + return $"releases/asset-store/{version.Replace('.', '-')}"; + } + return $"releases/{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($"Download failed with status code {response.StatusCode} and reason {response.ReasonPhrase}"); + } + var contentLength = response.Content.Headers.ContentLength; + if (!contentLength.HasValue) { + throw new DownloadException("Download failed: Content length unknown"); + } + + 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("Download failed: download file is corrupted"); + } + 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("Has to be readable", nameof(source)); + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + if (!destination.CanWrite) + throw new ArgumentException("Has to be writable", nameof(destination)); + if (bufferSize < 0) + throw new ArgumentOutOfRangeException(nameof(bufferSize)); + + var buffer = new byte[bufferSize]; + long totalBytesRead = 0; + int bytesRead; + 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 000000000..242f4640e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/DownloadUtility.cs + uploadId: 668105 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 000000000..95919721a --- /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 000000000..0bae16ee6 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/ExponentialBackoff.cs + uploadId: 668105 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 000000000..8bb802554 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs @@ -0,0 +1,58 @@ +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 { + const 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))) { + HotReloadWindow.Open(); + } + if (HotReloadPrefs.LaunchOnEditorStart) { + EditorCodePatcher.DownloadAndRun().Forget(); + } + + RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.Editor, StatEventType.Start)).Forget(); + } + + public static void CheckForNewInstall() { + if(File.Exists(installFlagPath)) { + 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(); + } + HotReloadWindow.Open(); + HotReloadPrefs.AllowDisableUnityAutoRefresh = true; + HotReloadPrefs.AllAssetChanges = true; + HotReloadPrefs.AutoRecompileUnsupportedChanges = true; + HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode = true; + 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 000000000..0f1b4a1f2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/InstallUtility.cs + uploadId: 668105 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 000000000..1bd631541 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs @@ -0,0 +1,190 @@ +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.Newtonsoft.Json; +using UnityEditor; +using UnityEngine; + +namespace SingularityGroup.HotReload.Editor { + internal class ServerDownloader : IProgress { + public float Progress {get; private set;} + public bool Started {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 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) { + var targetDir = CliUtils.GetExecutableTargetDir(); + var targetPath = Path.Combine(targetDir, cliController.BinaryFileName); + Started = true; + if(File.Exists(targetPath)) { + Progress = 1f; + return true; + } + Progress = 0f; + await ThreadUtility.SwitchToThreadPool(cancellationToken); + + Directory.CreateDirectory(targetDir); + if(TryUseUserDefinedBinaryPath(cliController, targetPath)) { + Progress = 1f; + return true; + } + + var tmpPath = CliUtils.GetTempDownloadFilePath("Server.tmp"); + var attempt = 0; + bool sucess = false; + HashSet errors = null; + while(!sucess) { + try { + if (File.Exists(targetPath)) { + Progress = 1f; + 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 (Exception e) { + var error = $"{e.GetType().Name}: {e.Message}"; + errors = (errors ?? new HashSet()); + if (errors.Add(error)) { + Log.Warning($"Download attempt failed. If the issue persists please reach out to customer support for assistance. Exception: {error}"); + } + } + if (!sucess) { + await Task.Delay(ExponentialBackoff.GetTimeout(attempt), cancellationToken).ConfigureAwait(false); + } + Progress = 0; + attempt++; + } + + 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("Download succeeded!"); + } + + 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; + return true; + } + + static bool TryUseUserDefinedBinaryPath(ICliController cliController, string targetPath) { + if (!File.Exists(PackageConst.ConfigFileName)) { + return false; + } + + var config = JsonConvert.DeserializeObject(File.ReadAllText(PackageConst.ConfigFileName)); + var customExecutables = config?.customServerExecutables; + if (customExecutables == null) { + return false; + } + + string customBinaryPath; + if(!customExecutables.TryGetValue(cliController.PlatformName, out customBinaryPath)) { + return false; + } + + if (!File.Exists(customBinaryPath)) { + Log.Warning($"unable to find server binary for platform '{cliController.PlatformName}' at '{customBinaryPath}'. " + + $"Will proceed with downloading the binary (default behavior)"); + 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("encountered exception when copying server binary in the specified custom executable path '{0}':\n{1}", customBinaryPath, ex); + return false; + } + } + + static string GetDownloadUrl(ICliController cliController) { + const string version = PackageConst.ServerVersion; + var key = $"{DownloadUtility.GetPackagePrefix(version)}/server/{cliController.PlatformName}/{cliController.BinaryFileName}"; + return DownloadUtility.GetDownloadUrl(key); + } + + void IProgress.Report(float value) { + Progress = value; + } + + public Task PromptForDownload() { + if (EditorUtility.DisplayDialog( + title: "Install platform specific components", + message: InstallDescription, + ok: "Install", + cancel: "More Info") + ) { + return EnsureDownloaded(HotReloadCli.controller, CancellationToken.None); + } + Application.OpenURL(Constants.AdditionalContentURL); + return Task.FromResult(false); + } + + public const string InstallDescription = "For Hot Reload to work, additional components specific to your operating system have to be installed"; + } + + 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 000000000..a97f48181 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/ServerDownloader.cs + uploadId: 668105 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 000000000..fbf393dc7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs @@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Editor.Cli; +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 "unable to locate hot reload package"; + } + var packageDir = Path.GetDirectoryName(Path.GetFullPath(serverDir)); + var cacheDir = Path.GetFullPath(PackageConst.LibraryCachePath); + if(Path.GetPathRoot(packageDir) != Path.GetPathRoot(cacheDir)) { + progress?.Report(1); + return "unable to update package because it is located on a different drive than the unity project"; + } + var updatedPackageCopy = BackupPackage(packageDir, version); + + var key = $"{DownloadUtility.GetPackagePrefix(version)}/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 000000000..13700b178 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Installation/UpdateUtility.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild.meta b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild.meta new file mode 100644 index 000000000..d600ba4e5 --- /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 000000000..573ef1bda --- /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()) { + 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 000000000..560e72b4b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs + uploadId: 668105 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 000000000..8c553f266 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using UnityEditor; + +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 + }; + + public static bool IsMonoSupported(BuildTargetGroup buildTarget) { + // "When a platform can support both backends, Mono is the default. For more information, see Scripting restrictions." + // Unity docs https://docs.unity3d.com/Manual/Mono.html (2019.4/2020.3/2021.3) +#pragma warning disable CS0618 // obsolete since 2023 + var defaultScripting = PlayerSettings.GetDefaultScriptingBackend(buildTarget); +#pragma warning restore CS0618 + if (defaultScripting == ScriptingImplementation.Mono2x) { + return Array.IndexOf(unsupportedBuildTargets, buildTarget) == -1; + } + // default scripting was not Mono, so the platform doesn't support Mono at all. + return false; + } + } +} \ 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 000000000..8994fdabd --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs + uploadId: 668105 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 000000000..17e03c67f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs @@ -0,0 +1,133 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; +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($"[{CodePatcher.TAG}] Unable to find {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("Did not find {0} at {1}, searching for manifest file inside {2}", 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 000000000..2b1d28b64 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs + uploadId: 668105 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 000000000..c28b406d7 --- /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()) { + 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 000000000..d26597cd4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs + uploadId: 668105 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 000000000..40081e835 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs @@ -0,0 +1,60 @@ +using System; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +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) { + 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("Hot Reload was not included in the build because one or more build settings were not supported."); + } + } + + // 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 000000000..49b143c7f --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration.meta b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration.meta new file mode 100644 index 000000000..f6f2a3a63 --- /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 000000000..2af8f5ab6 --- /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 000000000..0ed786468 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/FileIOProvider.cs + uploadId: 668105 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 000000000..d55e241ea --- /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 000000000..0743ef254 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/GUIDProvider.cs + uploadId: 668105 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 000000000..0cda7f54a --- /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 000000000..51bf2743c --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IFileIO.cs + uploadId: 668105 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 000000000..46bde64d9 --- /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 000000000..33d8767ce --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IGUIDGenerator.cs + uploadId: 668105 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 000000000..de473c01d --- /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 000000000..e5d7b170a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/IHotReloadProjectGenerationPostProcessor.cs + uploadId: 668105 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 000000000..17856eb4f --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs @@ -0,0 +1,14 @@ +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() { + 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 000000000..7479d607e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenenerationPostProcessor.cs + uploadId: 668105 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 000000000..cb2d3bfb3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs @@ -0,0 +1,893 @@ +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.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 const 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 (!IsSyncing) { + return GenerateAsync(dataPath); + } + return Task.CompletedTask; + } + + public static Task EnsureSlnAndCsprojFiles(string dataPath) { + 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(); + } + + public 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); + } + + public 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("Post processor '{0}' threw exception when calling OnGeneratedCSProjectFilesThreaded:\n{1}", 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("Post processor '{0}' failed when processing project '{1}':\n{2}", 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("Post processor '{0}' failed when processing solution '{1}':\n{2}", 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( + "Failed creating c# project because the c# project header did not have the correct amount of arguments, which is " + + 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) + // #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, 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("The type '{0}' was expected to have a public default constructor but it didn't", type.FullName); + } catch (TargetInvocationException ex) { + Log.Warning("Exception occurred when invoking default constructor of '{0}':\n{1}", type.FullName, ex.InnerException); + } catch (Exception ex) { + Log.Warning("Unknown exception encountered when trying to create post processor '{0}':\n{1}", 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 000000000..3b0dfe4f3 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGeneration.cs + uploadId: 668105 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 000000000..ac6781f76 --- /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 000000000..bd530b2ac --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectGenerationFlag.cs + uploadId: 668105 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 000000000..4f2fa3a80 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +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("{0} Parse Error : {1}", 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 000000000..9a107b7b0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/ProjectPart.cs + uploadId: 668105 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 000000000..14175313d --- /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 000000000..29e2e5f4a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/SolutionGuidGenerator.cs + uploadId: 668105 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 000000000..f4d773e69 --- /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 000000000..c58df02d1 --- /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 000000000..e645eaf22 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/FileSystemUtil.cs + uploadId: 668105 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 000000000..486a32a31 --- /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 000000000..9672eec45 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ProjectGeneration/Util/StringUtils.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings.meta b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings.meta new file mode 100644 index 000000000..2fd152d2e --- /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 000000000..6177dcfc2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +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 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($"Detouring {original.Name} method failed. {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 000000000..f23e2dc84 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/RequiredSettings/RequiredSettingChecker.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Resources.meta b/Packages/com.singularitygroup.hotreload/Editor/Resources.meta new file mode 100644 index 000000000..f14fddd68 --- /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 000000000..a27133ced 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 000000000..46d0a8221 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_Stop.png + uploadId: 668105 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 000000000..429e1be8b 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 000000000..e7182ec3a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/Icn_play.png + uploadId: 668105 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 000000000..5f144aa01 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 000000000..7eff666f4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_DarkMode.png + uploadId: 668105 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 000000000..bfaeb4a8e 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 000000000..9bc379bc9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/Logo_HotReload_LightMode.png + uploadId: 668105 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 000000000..f17c65b1c 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 000000000..e5a9efdce --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/alert_info.png + uploadId: 668105 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 000000000..dc4552c82 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 000000000..9d42c94fb --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/applied_patch.png + uploadId: 668105 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 000000000..a16c26e18 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 000000000..bad308b51 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/blue.png + uploadId: 668105 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 000000000..fc5f16ccc 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 000000000..2a085e108 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/close.png + uploadId: 668105 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 000000000..f5a0bd7b4 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 000000000..cac9363e9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/error.png + uploadId: 668105 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 000000000..0cbc27251 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 000000000..27f2597d2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/events.png + uploadId: 668105 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 000000000..ad482a11e 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 000000000..bd92761ab --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_closed.png + uploadId: 668105 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 000000000..928bbfaa9 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 000000000..3695bbf78 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/foldout_open.png + uploadId: 668105 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 000000000..1c3184c5d 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 000000000..c9abe1df0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/green.png + uploadId: 668105 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 000000000..786e5306d 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 000000000..c9146766d --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/grey.png + uploadId: 668105 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 000000000..7f99562cf 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 000000000..ca0cb53c0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/icon_check_circle.png + uploadId: 668105 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 000000000..53c220086 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 000000000..48fe1f466 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_black.png + uploadId: 668105 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 000000000..fac107070 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 000000000..4628b654c --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/icon_circle_gray.png + uploadId: 668105 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 000000000..317ba373b 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 000000000..7755bf2a2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/icon_loading_star_light_mode_96.png + uploadId: 668105 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 000000000..9c4d478e6 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 000000000..26ad77ad1 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/icon_warning_circle.png + uploadId: 668105 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 000000000..5a8ed9b22 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 000000000..be04ff080 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/infos.png + uploadId: 668105 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 000000000..7707eadb7 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 000000000..b2e184196 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/logo.png + uploadId: 668105 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 000000000..44c2c9bf7 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 000000000..8c4979543 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/red.png + uploadId: 668105 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 000000000..5c43f676b 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 000000000..e8e95ab2d --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/red_dot.png + uploadId: 668105 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 000000000..d5c57890c 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 000000000..0b2f1542b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/refresh.png + uploadId: 668105 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 000000000..9754cfbbe 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 000000000..177bd7814 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/report_bug.png + uploadId: 668105 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 000000000..b1ad63e45 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 000000000..076de39b2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/warning.png + uploadId: 668105 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 000000000..4d81c86d2 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 000000000..7b30722e6 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Resources/yellow.png + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs new file mode 100644 index 000000000..f5fed8860 --- /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 000000000..0a8c2fcce --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/ServerHealthCheck.cs + uploadId: 668105 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 000000000..fa46fc617 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef @@ -0,0 +1,28 @@ +{ + "name": "SingularityGroup.HotReload.Editor", + "rootNamespace": "SingularityGroup.HotReload", + "references": [ + "SingularityGroup.HotReload.Runtime.Public", + "SingularityGroup.HotReload.Runtime", + "UnityEditor.UI", + "UnityEngine.UI", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" + ], + "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" + ], + "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 000000000..4e4a29220 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.Editor.asmdef + uploadId: 668105 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 000000000..1ab9b41e1 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 000000000..7aabdab2a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll.meta @@ -0,0 +1,93 @@ +fileFormatVersion: 2 +guid: 2286e58e1d144b84c925391e896974b2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 1 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 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: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - 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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/SingularityGroup.HotReload.EditorDependencies.dll + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Editor/Versioning.meta b/Packages/com.singularitygroup.hotreload/Editor/Versioning.meta new file mode 100644 index 000000000..0477de076 --- /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 000000000..cc1281181 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs @@ -0,0 +1,288 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.Editor.Semver; +using SingularityGroup.HotReload.Newtonsoft.Json; +using SingularityGroup.HotReload.Newtonsoft.Json.Linq; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; + +namespace SingularityGroup.HotReload.Editor { + internal class PackageUpdateChecker { + const string persistedFile = PackageConst.LibraryCachePath + "/updateChecker.json"; + readonly JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(); + SemVersion newVersionDetected; + bool started; + + 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("encountered exception when checking for new Hot Reload package version:\n{0}", 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 { + Log.Warning("version check failed: {0}", response.err); + } + } 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.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($"Request failed with statusCode: {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("Invalid package.json"); + } else if(!SemVersion.TryParse(value.Value(), out newVersion, strict: true)) { + return Response.FromError($"Invalid version in package.json: '{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($"Update To v{newVersion}", $"By pressing 'Update' the Hot Reload package will be updated to v{newVersion}", "Update", "Cancel")) { + 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("Encountered issue when updating Hot Reload: {0}", 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("Failed to update package: {0}", 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 "Unable to find manifest.json"; + } + + 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 "no dependencies object found in manifest.json"; + } + deps = value.Value(); + if(deps == null) { + return "dependencies object null in manifest.json"; + } + return null; + } + + static async Task IsUsingGitRepo() { + var respose = await Task.Run(() => IsUsingGitRepoThreaded(PackageConst.PackageName)); + if(respose.err != null) { + Log.Warning("Unable to find package. message: {0}", respose.err); + return false; + } else { + return respose.data; + } + } + + static Response IsUsingGitRepoThreaded(string packageId) { + var fi = new FileInfo("Packages/manifest.json"); + if(!fi.Exists) { + return "Unable to find manifest.json"; + } + + using(var reader = fi.OpenText()) { + var root = JObject.Load(new JsonTextReader(reader)); + JObject deps; + var err = TryGetManfestDeps(root, out deps); + if(err != null) { + return "no dependencies specified in manifest.json"; + } + 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 000000000..f28198310 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Versioning/PackageUpdateChecker.cs + uploadId: 668105 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 000000000..72d9854e3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs @@ -0,0 +1,536 @@ +using System; +#if !NETSTANDARD +using System.Globalization; +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif +using System.Text.RegularExpressions; + +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("Invalid version (no minor version given in strict mode)");
+            }
+
+            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("Invalid version (no patch version given in strict mode)");
+            }
+
+            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 000000000..b4017da8a
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Versioning/SemVer.cs
+  uploadId: 668105
diff --git a/Packages/com.singularitygroup.hotreload/Editor/Window.meta b/Packages/com.singularitygroup.hotreload/Editor/Window.meta
new file mode 100644
index 000000000..710dd15a1
--- /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 000000000..5cbd648a3
--- /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 000000000..4d51a80f8
--- /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 000000000..3ecfc201e
--- /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 000000000..738e1c437
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenDialogueButton.cs
+  uploadId: 668105
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 000000000..0f1edcc90
--- /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 000000000..608751c7f
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Buttons/OpenURLButton.cs
+  uploadId: 668105
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 000000000..d20fae9bf
--- /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 000000000..8f364a823
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/EditorTextures.cs
+  uploadId: 668105
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 000000000..323ce59cb
--- /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 000000000..7cdb56e08
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/IGUIComponent.cs
+  uploadId: 668105
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 000000000..32dff3ddb
--- /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 000000000..bcdedf81d
--- /dev/null
+++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
@@ -0,0 +1,48 @@
+using UnityEditor;
+
+namespace SingularityGroup.HotReload.Editor {
+    internal class AllowAndroidAppToMakeHttpRequestsOption : ProjectOptionBase {
+        public override string ShortSummary {
+            get {
+                return "Allow app to make HTTP requests";
+            }
+        }
+
+        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 = "For Hot Reload to work on-device, please allow HTTP requests";
+            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 000000000..828ae0334
--- /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.12.10
+  assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
+  uploadId: 668105
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 000000000..a16bb7046
--- /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 000000000..8cfa45772
--- /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 000000000..2b0907f36 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs + uploadId: 668105 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 000000000..e68f3a1b2 --- /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 000000000..a1e0d668e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs + uploadId: 668105 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 000000000..7d225e8b6 --- /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 UnityEditor; + +namespace SingularityGroup.HotReload.Editor { + internal sealed class ExposeServerOption : ComputerOptionBase { + + public override string ShortSummary => "Allow Devices to Connect"; + public override string Summary => "Allow Devices to Connect (WiFi)"; + + public override void InnerOnGUI() { + string description; + if (GetValue()) { + description = "The HotReload server is reachable from devices on the same Wifi network"; + } else { + description = "The HotReload server is available to your computer only. Other devices cannot connect to it."; + } + 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("Hot Reload", + $"When changing '{Summary}', the Hot Reload server must be restarted for this to take effect." + + "\nDo you want to restart it now?", + "Restart server", "Don't restart"); + 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 000000000..659944146 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs + uploadId: 668105 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 000000000..3a6813436 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs @@ -0,0 +1,24 @@ +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 => "Include Hot Reload in player builds"; + public override string Summary => ShortSummary; + + public override string ObjectPropertyName => + nameof(HotReloadSettingsObject.IncludeInBuild); + + public override void InnerOnGUI(SerializedObject so) { + string description; + if (GetValue(so)) { + description = "The Hot Reload runtime is included in development builds that use the Mono scripting backend."; + } else { + description = "The Hot Reload runtime will not be included in any build. Use this option to disable HotReload without removing it from your project."; + } + description += " This option does not affect Hot Reload usage in Playmode"; + 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 000000000..40e66fc59 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs + uploadId: 668105 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 000000000..b77723157 --- /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 000000000..9cec40c97 --- /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 000000000..7a648881a --- /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 000000000..1f3d433e8 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Base/HotReloadTabBase.cs + uploadId: 668105 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 000000000..0c5c6ba69 --- /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 000000000..a0c841f53 --- /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.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 const string registerFlagPath = PackageConst.LibraryCachePath + "/registerFlag.txt"; + public const 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"; + const string unknownError = "We apologize, an error happened while redeeming your license. Please reach out to customer support for assistance."; + + 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($"Failed determining registration outcome with {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 + ? "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." + : "The licensing model for Unity Pro users varies depending on the number of employees in your company. Please enter your company details below."; + if (error != null) { + EditorGUILayout.HelpBox(error, MessageType.Warning); + } else { + EditorGUILayout.HelpBox(message, MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Comany size (number of employees)"); + GUI.SetNextControlName("company_size"); + _pendingCompanySize = EditorGUILayout.TextField(_pendingCompanySize)?.Trim(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Proceed")) { + int companySize; + if (!int.TryParse(_pendingCompanySize, out companySize)) { + error = "Please enter a number."; + } 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("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.", MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Invoice number/Order ID"); + GUI.SetNextControlName("invoice_number"); + _pendingInvoiceNumber = EditorGUILayout.TextField(_pendingInvoiceNumber ?? HotReloadPrefs.RedeemLicenseInvoice)?.Trim(); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Email"); + GUI.SetNextControlName("email_redeem"); + _pendingRedeemEmail = EditorGUILayout.TextField(_pendingRedeemEmail ?? HotReloadPrefs.RedeemLicenseEmail); + EditorGUILayout.Space(); + + using (new EditorGUI.DisabledScope(requestingRedeem)) { + if (GUILayout.Button("Redeem", HotReloadRunTab.bigButtonHeight)) { + RedeemLicense(email: _pendingRedeemEmail, invoiceNumber: _pendingInvoiceNumber).Forget(); + } + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Skip", secondaryButtonLayoutOptions)) { + SwitchToStage(RedeemStage.Login); + } + GUILayout.FlexibleSpace(); + } + } + + async Task RedeemLicense(string email, string invoiceNumber) { + string validationError; + if (string.IsNullOrEmpty(invoiceNumber)) { + validationError = "Please enter invoice number / order ID."; + } 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("Redeeming license failed: unknown status received"); + error = unknownError; + } 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($"Redeeming a license failed with error: {resp.error}"); + error = GetPrettyError(resp); + } else { + Log.Warning("Redeeming a license failed: uknown error encountered"); + error = unknownError; + } + } + + string GetPrettyError(RedeemResponse response) { + var err = response?.error; + if (err == null) { + return unknownError; + } + if (err.Contains("Invalid email")) { + return "Please enter a valid email address."; + } else if (err.Contains("License invoice already redeemed")) { + return "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."; + } else if (err.Contains("Different license already redeemed by given email")) { + return "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."; + } else if (err.Contains("Invoice not found")) { + return "The invoice was not found. Please ensure that you've entered the correct invoice number/order ID."; + } else if (err.Contains("Invoice refunded")) { + return "The purchase has been refunded. Please enter a different invoice number/order ID."; + } else { + return unknownError; + } + } + + 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, $"Redeem request failed. Status code: {(int)resp.StatusCode}, reason: {resp.ReasonPhrase}"); + } + var str = await resp.Content.ReadAsStringAsync().ConfigureAwait(false); + try { + return JsonConvert.DeserializeObject(str); + } catch (Exception ex) { + return new RedeemResponse(null, $"Failed deserializing redeem response with exception: {ex.GetType().Name}: {ex.Message}"); + } + } + } catch (WebException ex) { + return new RedeemResponse(null, $"Redeeming license failed: WebException encountered {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("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.", MessageType.Info); + } else if (status == statusAlreadyClaimed) { + EditorGUILayout.HelpBox("Your license has already been redeemed. Please enter your existing password below.", MessageType.Info); + } + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + HotReloadRunTab.RenderLicenseInnerPanel(state, renderLogout: false); + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Go Back", 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 000000000..1f97908ba --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/Helpers/RedeemLicenseHelper.cs + uploadId: 668105 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 000000000..d2b6e95e8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +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("See More", Constants.ChangelogURL); + internal static readonly OpenDialogueButton manageLicenseButton = new OpenDialogueButton("Manage License", Constants.ManageLicenseURL, "Manage License", "Upgrade/downgrade/edit your subscription and edit payment info.", "Open in browser", "Cancel"); + internal static readonly OpenDialogueButton manageAccountButton = new OpenDialogueButton("Manage Account", Constants.ManageAccountURL, "Manage Account", "Login with company code 'naughtycult'. Use the email you signed up with. Your initial password was sent to you by email.", "Open in browser", "Cancel"); + internal static readonly OpenURLButton contactButton = new OpenURLButton("Contact", Constants.ContactURL); + internal static readonly OpenURLButton discordButton = new OpenURLButton("Join Discord", Constants.DiscordInviteUrl); + internal static readonly OpenDialogueButton reportIssueButton = new OpenDialogueButton("Report issue", Constants.ReportIssueURL, "Report issue", "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).", "Open in browser", "Cancel"); + + 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, "Help", "_Help", "Info and support for Hot Reload for Unity.") { } + + 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 "Today"; + + if (delta < 48 * hour) + return "Yesterday"; + + if (delta < 30 * day) + return ts.Days + " days ago"; + + if (delta < 12 * month) { + var months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); + return months <= 1 ? "one month ago" : months + " months ago"; + } + var years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); + return years <= 1 ? "one year ago" : years + " years ago"; + } + + 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("Features:", HotReloadWindowStyles.H2TitleStyle); + tempTextString = ""; + foreach (var feature in version.features) { + tempTextString += "• " + feature + "\n"; + } + EditorGUILayout.TextArea(tempTextString, HotReloadWindowStyles.ChangelogPointerStyle); + } + + //improvements + if (version.improvements != null) { + EditorGUILayout.TextArea("Improvements:", HotReloadWindowStyles.H2TitleStyle); + tempTextString = ""; + foreach (var improvement in version.improvements) { + tempTextString += "• " + improvement + "\n"; + } + EditorGUILayout.TextArea(tempTextString, HotReloadWindowStyles.ChangelogPointerStyle); + } + + //fixes + if (version.fixes != null) { + EditorGUILayout.TextArea("Fixes:", 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, "Changelog", 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($" Hot Reload version {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, "Documentation", 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 }, "Unity Forum", 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("Open Log File")) { + var mostRecentFile = LogsHelper.FindRecentLog(logsPath); + if (mostRecentFile == null) { + Log.Info("No logs found"); + } else { + try { + Process.Start($"\"{Path.Combine(logsPath, mostRecentFile)}\""); + } catch (Win32Exception e) { + 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("Failed opening log file."); + } + } + } catch { + // Fallback to opening folder with all logs + Process.Start($"\"{logsPath}\""); + Log.Info("Failed opening log file."); + } + } + } + if (GUILayout.Button("Browse all logs")) { + 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 000000000..5e7723f3b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadAboutTab.cs + uploadId: 668105 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 000000000..63b5c43c3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs @@ -0,0 +1,49 @@ +using UnityEditor; +using UnityEngine; + +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("Unexpected problem unable to save HotReloadSettingsObject"); + } 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 000000000..71040330a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadOptionsSection.cs + uploadId: 668105 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 000000000..6ce934132 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs @@ -0,0 +1,1367 @@ +using System; +using System.Collections.Generic; +using System.IO; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.EditorDependencies; +using UnityEditor; +using UnityEditor.Compilation; +using UnityEngine; +using Color = UnityEngine.Color; +using Task = System.Threading.Tasks.Task; +#if UNITY_2019_4_OR_NEWER +using Unity.CodeEditor; +#endif + +namespace SingularityGroup.HotReload.Editor { + internal class ErrorData { + public string fileName; + public string error; + public TextAsset file; + public int lineNumber; + public string stacktrace; + public string linkString; + private static string[] supportedPaths = new[] { Path.GetFullPath("Assets"), Path.GetFullPath("Plugins") }; + + public static ErrorData GetErrorData(string errorString) { + // Get the relevant file name + string stackTrace = errorString; + string fileName = null; + try { + int csIndex = 0; + int attempt = 0; + do { + csIndex = errorString.IndexOf(".cs", csIndex + 1, StringComparison.Ordinal); + if (csIndex == -1) { + break; + } + int fileNameStartIndex = csIndex - 1; + for (; fileNameStartIndex >= 0; fileNameStartIndex--) { + if (!char.IsLetter(errorString[fileNameStartIndex])) { + if (errorString.Contains("error CS")) { + fileName = errorString.Substring(fileNameStartIndex + 1, + csIndex - fileNameStartIndex + ".cs".Length - 1); + } else { + fileName = errorString.Substring(fileNameStartIndex, + csIndex - fileNameStartIndex + ".cs".Length); + } + break; + } + } + } while (attempt++ < 100 && fileName == null); + } catch { + // ignore + } + fileName = fileName ?? "Tap to show stacktrace"; + + // Get the error + string error = (errorString.Contains("error CS") + ? "Compile error, " + : "Unsupported change detected, ") + "tap here to see more."; + int endOfError = errorString.IndexOf(". in ", StringComparison.Ordinal); + string specialChars = "\"'/\\"; + char[] characters = specialChars.ToCharArray(); + int specialChar = errorString.IndexOfAny(characters); + try { + if (errorString.Contains("error CS") ) { + error = errorString.Substring(errorString.IndexOf("error CS", StringComparison.Ordinal), errorString.Length - errorString.IndexOf("error CS", StringComparison.Ordinal)).Trim(); + using (StringReader reader = new StringReader(error)) { + string line; + while ((line = reader.ReadLine()) != null) { + error = line; + break; + } + } + } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && endOfError > 0) { + error = errorString.Substring("errors: ".Length, endOfError - "errors: ".Length).Trim(); + } else if (errorString.StartsWith("errors:", StringComparison.Ordinal) && specialChar > 0) { + error = errorString.Substring("errors: ".Length, specialChar - "errors: ".Length).Trim(); + } + } catch { + // ignore + } + + // Get relative path + TextAsset file = null; + 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 candidateAbsolutePath = errorString.Substring(lastprojectIndex, fullCsIndex - lastprojectIndex + ".cs".Length); + var candidateRelativePath = EditorCodePatcher.GetRelativePath(filespec: candidateAbsolutePath, folder: path); + file = AssetDatabase.LoadAssetAtPath(candidateRelativePath); + } + } + + // 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, "Run", "forward", "Run and monitor the current Hot Reload session.") { } + + public override void OnGUI() { + using(new EditorGUILayout.VerticalScope()) { + OnGUICore(); + } + } + + internal static bool ShouldRenderConsumption(HotReloadRunTabState currentState) => (currentState.running && !currentState.starting && !currentState.stopping && currentState.loginStatus?.isLicensed != true && currentState.loginStatus?.isFree != true && !EditorCodePatcher.LoginNotRequired) && !(currentState.loginStatus == null || currentState.loginStatus.isFree); + + void OnGUICore() { + using (var scope = new EditorGUILayout.ScrollViewScope(_runTabScrollPos, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar, GUILayout.MaxHeight(Math.Max(HotReloadWindowStyles.windowScreenHeight, 800)), GUILayout.MaxWidth(Math.Max(HotReloadWindowStyles.windowScreenWidth, 800)))) { + _runTabScrollPos.x = scope.scrollPosition.x; + _runTabScrollPos.y = scope.scrollPosition.y; + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.DynamiSection)) { + if (HotReloadWindowStyles.windowScreenWidth > Constants.UpgradeLicenseNoteHideWidth + && HotReloadWindowStyles.windowScreenHeight > Constants.UpgradeLicenseNoteHideHeight + ) { + RenderUpgradeLicenseNote(currentState, HotReloadWindowStyles.UpgradeLicenseButtonStyle); + } + + RenderIndicationPanel(); + + if (CanRenderBars(currentState)) { + RenderBars(currentState); + // clear red dot next time button shows + HotReloadState.ShowingRedDot = false; + } + } + } + + // At the end to not fuck up rendering https://answers.unity.com/questions/400454/argumentexception-getting-control-0s-position-in-a-1.html + var renderStart = !EditorCodePatcher.Running && !EditorCodePatcher.Starting && !currentState.requestingDownloadAndRun && currentState.redeemStage == RedeemStage.None; + var e = Event.current; + if (renderStart && e.type == EventType.KeyUp + && (e.keyCode == KeyCode.Return + || e.keyCode == KeyCode.KeypadEnter) + ) { + EditorCodePatcher.DownloadAndRun().Forget(); + } + } + + internal static void RenderUpgradeLicenseNote(HotReloadRunTabState currentState, GUIStyle style) { + var isIndie = RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Indie + || EditorCodePatcher.licenseType == UnityLicenseType.UnityPersonalPlus; + + if (RedeemLicenseHelper.I.RegistrationOutcome == RegistrationOutcome.Business + && currentState.loginStatus?.isBusinessLicense != true + && EditorCodePatcher.Running + && (PackageConst.IsAssetStoreBuild || HotReloadPrefs.RateAppShown) + ) { + // Warn asset store users they need to buy a business license + // Website users get reminded after using Hot Reload for 5+ days + RenderBusinessLicenseInfo(style); + } else if (isIndie + && HotReloadPrefs.RateAppShown + && !PackageConst.IsAssetStoreBuild + && EditorCodePatcher.Running + && currentState.loginStatus?.isBusinessLicense != true + && currentState.loginStatus?.isIndieLicense != true + ) { + // Reminder users they need to buy an indie license + RenderIndieLicenseInfo(style); + } + } + + internal static bool CanRenderBars(HotReloadRunTabState currentState) { + return HotReloadWindowStyles.windowScreenHeight > Constants.EventsListHideHeight + && HotReloadWindowStyles.windowScreenWidth > Constants.EventsListHideWidth + && !currentState.starting + && !currentState.stopping + && !currentState.requestingDownloadAndRun + ; + } + + static Texture2D GetFoldoutIcon(AlertEntry alertEntry) { + InvertibleIcon alertIcon = InvertibleIcon.FoldoutClosed; + if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) { + alertIcon = InvertibleIcon.FoldoutOpen; + } + return GUIHelper.GetInvertibleIcon(alertIcon); + } + + static void ToggleEntry(AlertEntry alertEntry) { + if (HotReloadTimelineHelper.expandedEntries.Contains(alertEntry)) { + HotReloadTimelineHelper.expandedEntries.Remove(alertEntry); + } else { + HotReloadTimelineHelper.expandedEntries.Add(alertEntry); + } + } + + static void RenderEntries(TimelineType timelineType) { + List alertEntries; + + alertEntries = timelineType == TimelineType.Suggestions ? HotReloadTimelineHelper.Suggestions : HotReloadTimelineHelper.EventsTimeline; + + bool skipChildren = false; + for (int i = 0; i < alertEntries.Count; i++) { + var alertEntry = alertEntries[i]; + if (i > HotReloadTimelineHelper.maxVisibleEntries && alertEntry.entryType != EntryType.Child) { + break; + } + if (timelineType != TimelineType.Suggestions) { + if (alertEntry.entryType != EntryType.Child + && !enabledFilters.Contains(alertEntry.alertType) + ) { + skipChildren = true; + continue; + } else if (alertEntry.entryType == EntryType.Child && skipChildren) { + continue; + } else { + skipChildren = false; + } + } + + EntryType entryType = alertEntry.entryType; + + string title = $" {alertEntry.title}{(!string.IsNullOrEmpty(alertEntry.shortDescription) ? $": {alertEntry.shortDescription}": "")}"; + Texture2D icon = null; + GUIStyle style; + if (entryType != EntryType.Child) { + icon = GUIHelper.GetLocalIcon(HotReloadTimelineHelper.alertIconString[alertEntry.iconType]); + } + if (entryType == EntryType.Child) { + style = HotReloadWindowStyles.ChildBarStyle; + } else if (entryType == EntryType.Foldout) { + style = HotReloadWindowStyles.FoldoutBarStyle; + } else { + style = HotReloadWindowStyles.BarStyle; + } + + Rect startRect; + using (new EditorGUILayout.HorizontalScope()) { + GUILayout.Space(0); + Rect spaceRect = GUILayoutUtility.GetLastRect(); + // entry header foldout arrow + if (entryType == EntryType.Foldout) { + GUI.Label(new Rect(spaceRect.x + 3, spaceRect.y, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry))); + } else if (entryType == EntryType.Child) { + GUI.Label(new Rect(spaceRect.x + 26, spaceRect.y + 2, 20, 20), new GUIContent(GetFoldoutIcon(alertEntry))); + } + // a workaround to limit the width of the label + GUILayout.Label(new GUIContent(""), style); + startRect = GUILayoutUtility.GetLastRect(); + GUI.Label(startRect, new GUIContent(title, icon), style); + } + + bool clickableDescription = alertEntry.title == "Unsupported change" || alertEntry.title == "Compile error" || alertEntry.title == "Failed applying patch to method"; + + 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) { + GUILayout.Label(alertEntry.description, HotReloadWindowStyles.LabelStyle); + } else if (!clickableDescription) { + string text = alertEntry.description; + GUILayout.TextArea(text, HotReloadWindowStyles.StacktraceTextAreaStyle); + } + if (alertEntry.actionData != null) { + alertEntry.actionData.Invoke(); + } + GUILayout.Space(5f); + } + } + } + } + + // remove button + if (timelineType == TimelineType.Suggestions && alertEntry.hasExitButton) { + var isClick = GUI.Button(new Rect(startRect.x + startRect.width - 20, startRect.y + 2, 20, 20), new GUIContent(GUIHelper.GetInvertibleIcon(InvertibleIcon.Close)), HotReloadWindowStyles.RemoveIconStyle); + if (isClick) { + HotReloadTimelineHelper.EventsTimeline.Remove(alertEntry); + var kind = HotReloadSuggestionsHelper.FindSuggestionKind(alertEntry); + if (kind != null) { + HotReloadSuggestionsHelper.SetSuggestionInactive((HotReloadSuggestionKind)kind); + } + _instantRepaint = true; + } + } + + // Extend background to whole entry + var endRect = GUILayoutUtility.GetLastRect(); + if (GUI.Button(new Rect(startRect) { height = endRect.y - startRect.y + endRect.height}, new GUIContent(""), HotReloadWindowStyles.BarBackgroundStyle) && (entryType == EntryType.Child || entryType == EntryType.Foldout)) { + ToggleEntry(alertEntry); + } + + if (alertEntry.alertType != AlertType.Suggestion && HotReloadWindowStyles.windowScreenWidth > 400 && entryType != EntryType.Child) { + using (new EditorGUILayout.HorizontalScope()) { + GUI.Label(new Rect(startRect.x + startRect.width - 60, startRect.y, 80, 20), $"{alertEntry.timestamp.Hour:D2}:{alertEntry.timestamp.Minute:D2}:{alertEntry.timestamp.Second:D2}", 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.RunTabAppliedPatchesFilter && !_enabledFilters.Contains(AlertType.AppliedChange)) + _enabledFilters.Add(AlertType.AppliedChange); + if (!HotReloadPrefs.RunTabAppliedPatchesFilter && _enabledFilters.Contains(AlertType.AppliedChange)) + _enabledFilters.Remove(AlertType.AppliedChange); + + return _enabledFilters; + } + } + + private Vector2 suggestionsScroll; + static GUILayoutOption[] timelineButtonOptions = new[] { GUILayout.Height(27), GUILayout.Width(100) }; + + internal static void RenderBars(HotReloadRunTabState currentState) { + if (currentState.suggestionCount > 0) { + GUILayout.Space(5f); + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.RunTabEventsSuggestionsFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsSuggestionsFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle); + GUILayout.Space(-23); + if (GUILayout.Button($"Suggestions ({currentState.suggestionCount.ToString()})", HotReloadWindowStyles.ClickableLabelBoldStyle, GUILayout.Height(27))) { + HotReloadPrefs.RunTabEventsSuggestionsFoldout = !HotReloadPrefs.RunTabEventsSuggestionsFoldout; + } + if (HotReloadPrefs.RunTabEventsSuggestionsFoldout) { + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Scroll)) { + RenderEntries(TimelineType.Suggestions); + } + } + } + } + } + GUILayout.Space(5f); + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.Section)) { + using (new EditorGUILayout.VerticalScope()) { + HotReloadPrefs.RunTabEventsTimelineFoldout = EditorGUILayout.Foldout(HotReloadPrefs.RunTabEventsTimelineFoldout, "", true, HotReloadWindowStyles.CustomFoldoutStyle); + GUILayout.Space(-23); + if (GUILayout.Button("Timeline", HotReloadWindowStyles.ClickableLabelBoldStyle, timelineButtonOptions)) { + HotReloadPrefs.RunTabEventsTimelineFoldout = !HotReloadPrefs.RunTabEventsTimelineFoldout; + } + if (HotReloadPrefs.RunTabEventsTimelineFoldout) { + GUILayout.Space(-10); + var noteShown = HotReloadTimelineHelper.GetRunTabTimelineEventCount() == 0 || !currentState.running; + using (new EditorGUILayout.HorizontalScope()) { + if (noteShown) { + GUILayout.Space(2f); + using (new EditorGUILayout.VerticalScope()) { + GUILayout.Space(2f); + string text; + if (currentState.redeemStage != RedeemStage.None) { + text = "Complete registration before using Hot Reload"; + } else if (!currentState.running) { + text = "Use the Start button to activate Hot Reload"; + } else if (enabledFilters.Count < 4 && HotReloadTimelineHelper.EventsTimeline.Count != 0) { + text = "Enable filters to see events"; + } else { + text = "Make code changes to see events"; + } + GUILayout.Label(text, HotReloadWindowStyles.EmptyListText); + } + GUILayout.FlexibleSpace(); + } else { + GUILayout.FlexibleSpace(); + if (HotReloadTimelineHelper.EventsTimeline.Count > 0 && GUILayout.Button("Clear")) { + HotReloadTimelineHelper.ClearEntries(); + if (HotReloadWindow.Current) { + HotReloadWindow.Current.Repaint(); + } + } + GUILayout.Space(3); + } + } + if (!noteShown) { + GUILayout.Space(2f); + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Scroll)) { + RenderEntries(TimelineType.Timeline); + } + } + } + } + } + } + + internal static void RenderConsumption(LoginStatusResponse loginStatus) { + if (loginStatus == null) { + return; + } + EditorGUILayout.Space(); + + EditorGUILayout.LabelField($"Hot Reload Limited", HotReloadWindowStyles.H3CenteredTitleStyle); + EditorGUILayout.Space(); + if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.NetworkUnreachable) { + EditorGUILayout.HelpBox("Something went wrong. Please check your internet connection.", MessageType.Warning); + } else if (loginStatus.consumptionsUnavailableReason == ConsumptionsUnavailableReason.UnrecoverableError) { + EditorGUILayout.HelpBox("Something went wrong. Please contact support if the issue persists.", MessageType.Error); + } else if (loginStatus.freeSessionFinished) { + var now = DateTime.UtcNow; + var sessionRefreshesAt = (now.AddDays(1).Date - now).Add(TimeSpan.FromMinutes(5)); + var sessionRefreshString = $"Next Session: {(sessionRefreshesAt.Hours > 0 ? $"{sessionRefreshesAt.Hours}h " : "")}{sessionRefreshesAt.Minutes}min"; + HotReloadGUIHelper.HelpBox(sessionRefreshString, MessageType.Warning, fontSize: 11); + } else if (loginStatus.freeSessionRunning && loginStatus.freeSessionEndTime != null) { + var sessionEndsAt = loginStatus.freeSessionEndTime.Value - DateTime.Now; + var sessionString = $"Daily Session: {(sessionEndsAt.Hours > 0 ? $"{sessionEndsAt.Hours}h " : "")}{sessionEndsAt.Minutes}min Left"; + HotReloadGUIHelper.HelpBox(sessionString, MessageType.Info, fontSize: 11); + } else if (loginStatus.freeSessionEndTime == null) { + HotReloadGUIHelper.HelpBox("Daily Session: Make code changes to start", MessageType.Info, fontSize: 11); + } + } + + static bool _repaint; + static bool _instantRepaint; + static DateTime _lastRepaint; + private EditorIndicationState.IndicationStatus _lastStatus; + public override void Update() { + if (EditorIndicationState.SpinnerActive) { + _repaint = true; + } + if (EditorCodePatcher.DownloadRequired) { + _repaint = true; + } + if (EditorIndicationState.IndicationIconPath == Spinner.SpinnerIconPath) { + _repaint = true; + } + try { + // workaround: hovering over non-buttons doesn't repain by default + if (EditorWindow.mouseOverWindow == HotReloadWindow.Current) { + _repaint = true; + } + if (EditorWindow.mouseOverWindow + && EditorWindow.mouseOverWindow?.GetType() == typeof(PopupWindow) + && HotReloadEventPopup.I.open + ) { + _repaint = true; + } + } catch (NullReferenceException) { + // Unity randomly throws nullrefs when EditorWindow.mouseOverWindow gets accessed + } + if (_repaint && DateTime.UtcNow - _lastRepaint > TimeSpan.FromMilliseconds(33)) { + _repaint = false; + _instantRepaint = true; + } + // repaint on status change + var status = EditorIndicationState.CurrentIndicationStatus; + if (_lastStatus != status) { + _lastStatus = status; + _instantRepaint = true; + } + if (_instantRepaint) { + Repaint(); + HotReloadEventPopup.I.Repaint(); + _instantRepaint = false; + _repaint = false; + _lastRepaint = DateTime.UtcNow; + } + } + + public static void RepaintInstant() { + _instantRepaint = true; + } + + private void RenderRecompileButton() { + string recompileText = HotReloadWindowStyles.windowScreenWidth > Constants.RecompileButtonTextHideWidth ? " Recompile" : ""; + var recompileButton = new GUIContent(recompileText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Recompile)); + if (!GUILayout.Button(recompileButton, HotReloadWindowStyles.RecompileButton)) { + return; + } + RecompileWithChecks(); + } + + public static void RecompileWithChecks() { + var firstDialoguePass = HotReloadPrefs.RecompileDialogueShown + || EditorUtility.DisplayDialog( + title: "Hot Reload auto-applies changes", + message: "Using the Recompile button is only necessary when Hot Reload fails to apply your changes. \n\nDo you wish to proceed?", + ok: "Recompile", + cancel: "Not now"); + HotReloadPrefs.RecompileDialogueShown = true; + if (!firstDialoguePass) { + return; + } + var secondDialoguePass = !Application.isPlaying + || EditorUtility.DisplayDialog( + title: "Stop Play Mode and Recompile?", + message: "Using the Recompile button will stop Play Mode.\n\nDo you wish to proceed?", + ok: "Stop and Recompile", + cancel: "Cancel"); + if (!secondDialoguePass) { + return; + } + Recompile(); + } + + public static bool recompiling; + public static void Recompile() { + recompiling = true; + EditorApplication.isPlaying = false; + + CompileMethodDetourer.Reset(); + AssetDatabase.Refresh(); + // This forces the recompilation if no changes were made. + // This is better UX because otherwise the recompile button is unresponsive + // which can be extra annoying if there are compile error entries in the list + if (!EditorApplication.isCompiling) { + CompilationPipeline.RequestScriptCompilation(); + } + } + + private void RenderIndicationButtons() { + if (currentState.requestingDownloadAndRun || currentState.starting || currentState.stopping || currentState.redeemStage != RedeemStage.None) { + return; + } + + if (!currentState.running && (currentState.startupProgress?.Item1 ?? 0) == 0) { + string startText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? " Start" : ""; + if (GUILayout.Button(new GUIContent(startText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Start)), HotReloadWindowStyles.StartButton)) { + EditorCodePatcher.DownloadAndRun().Forget(); + } + } else if (currentState.running && !currentState.starting) { + if (HotReloadWindowStyles.windowScreenWidth > 150 && HotReloadTimelineHelper.CompileErrorsCount == 0) { + RenderRecompileButton(); + } + string stopText = HotReloadWindowStyles.windowScreenWidth > Constants.StartButtonTextHideWidth ? " Stop" : ""; + if (GUILayout.Button(new GUIContent(stopText, GUIHelper.GetInvertibleIcon(InvertibleIcon.Stop)), HotReloadWindowStyles.StopButton)) { + if (!EditorCodePatcher.StoppedServerRecently()) { + EditorCodePatcher.StopCodePatcher().Forget(); + } + } + } + } + + void RenderIndicationPanel() { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.SectionInnerBox)) { + RenderIndication(); + if (HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + RenderIndicationButtons(); + if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + } + if (currentState.requestingDownloadAndRun || currentState.starting) { + RenderProgressBar(); + } + if (HotReloadWindowStyles.windowScreenWidth > Constants.ConsumptionsHideWidth + && HotReloadWindowStyles.windowScreenHeight > Constants.ConsumptionsHideHeight + ) { + RenderLicenseInfo(currentState); + } + } + + internal static void RenderLicenseInfo(HotReloadRunTabState currentState) { + var showRedeem = currentState.redeemStage != RedeemStage.None; + var showConsumptions = ShouldRenderConsumption(currentState); + if (!showConsumptions && !showRedeem) { + return; + } + using (new EditorGUILayout.VerticalScope()) { + // space needed only for consumptions because of Stop/Start button's margin + if (showConsumptions) { + GUILayout.Space(6); + } + using (new EditorGUILayout.VerticalScope(HotReloadWindowStyles.Section)) { + if (showRedeem) { + RedeemLicenseHelper.I.RenderStage(currentState); + } else { + RenderConsumption(currentState.loginStatus); + GUILayout.Space(10); + RenderLicenseInfo(currentState, currentState.loginStatus); + RenderLicenseButtons(currentState); + GUILayout.Space(10); + } + } + GUILayout.Space(6); + } + } + + private Spinner _spinner = new Spinner(85); + private void RenderIndication() { + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationBox)) { + // icon box + if (HotReloadWindowStyles.windowScreenWidth <= Constants.IndicationTextHideWidth) { + GUILayout.FlexibleSpace(); + } + + using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.IndicationHelpBox)) { + var text = HotReloadWindowStyles.windowScreenWidth > Constants.IndicationTextHideWidth ? $" {currentState.indicationStatusText}" : ""; + if (currentState.indicationIconPath == Spinner.SpinnerIconPath) { + GUILayout.Label(new GUIContent(text, _spinner.GetIcon()), style: HotReloadWindowStyles.IndicationIcon); + } else if (currentState.indicationIconPath != null) { + var style = HotReloadWindowStyles.IndicationIcon; + if (HotReloadTimelineHelper.alertIconString.ContainsValue(currentState.indicationIconPath)) { + style = HotReloadWindowStyles.IndicationAlertIcon; + } + GUILayout.Label(new GUIContent(text, GUIHelper.GetLocalIcon(currentState.indicationIconPath)), style); + } + } + } + } + + static GUIStyle _openSettingsStyle; + static GUIStyle openSettingsStyle => _openSettingsStyle ?? (_openSettingsStyle = new GUIStyle(GUI.skin.button) { + fontStyle = FontStyle.Normal, + fixedHeight = 25, + }); + + static GUILayoutOption[] _bigButtonHeight; + public static GUILayoutOption[] bigButtonHeight => _bigButtonHeight ?? (_bigButtonHeight = new [] {GUILayout.Height(25)}); + + private static GUIContent indieLicenseContent; + private static GUIContent businessLicenseContent; + + internal static void RenderLicenseStatusInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool allowHide = true, bool verbose = false) { + string message = null; + MessageType messageType = default(MessageType); + Action customGUI = null; + GUIContent content = null; + if (loginStatus == null) { + // no info + } else if (loginStatus.lastLicenseError != null) { + messageType = !loginStatus.freeSessionFinished ? MessageType.Warning : MessageType.Error; + message = GetMessageFromError(currentState, loginStatus.lastLicenseError); + } else if (loginStatus.isTrial && !PackageConst.IsAssetStoreBuild) { + message = $"Using Trial license, valid until {loginStatus.licenseExpiresAt.ToShortDateString()}"; + messageType = MessageType.Info; + } else if (loginStatus.isIndieLicense) { + if (verbose) { + message = " Indie license active"; + messageType = MessageType.Info; + customGUI = () => { + if (loginStatus.licenseExpiresAt.Date != DateTime.MaxValue.Date) { + EditorGUILayout.LabelField($"License will renew on {loginStatus.licenseExpiresAt.ToShortDateString()}."); + EditorGUILayout.Space(); + } + using (new GUILayout.HorizontalScope()) { + HotReloadAboutTab.manageLicenseButton.OnGUI(); + HotReloadAboutTab.manageAccountButton.OnGUI(); + } + EditorGUILayout.Space(); + }; + if (indieLicenseContent == null) { + indieLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed")); + } + content = indieLicenseContent; + } + } else if (loginStatus.isBusinessLicense) { + if (verbose) { + message = " Business license active"; + messageType = MessageType.Info; + if (businessLicenseContent == null) { + businessLicenseContent = new GUIContent(message, EditorGUIUtility.FindTexture("TestPassed")); + } + content = businessLicenseContent; + customGUI = () => { + using (new GUILayout.HorizontalScope()) { + HotReloadAboutTab.manageLicenseButton.OnGUI(); + HotReloadAboutTab.manageAccountButton.OnGUI(); + } + EditorGUILayout.Space(); + }; + } + } + + if (messageType != MessageType.Info && HotReloadPrefs.ErrorHidden && allowHide) { + return; + } + if (message != null) { + if (messageType != MessageType.Info) { + using(new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(message, messageType); + var style = HotReloadWindowStyles.HideButtonStyle; + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (allowHide) { + if (GUILayout.Button("Hide", style)) { + HotReloadPrefs.ErrorHidden = true; + } + } + } + } else if (content != null) { + EditorGUILayout.LabelField(content); + EditorGUILayout.Space(); + } else { + EditorGUILayout.LabelField(message); + EditorGUILayout.Space(); + } + customGUI?.Invoke(); + } + } + + const string assetStoreProInfo = "Unity Pro/Enterprise users from company with your number of employees require a Business license. Please upgrade your license on our website."; + internal static void RenderBusinessLicenseInfo(GUIStyle style) { + GUILayout.Space(8); + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(assetStoreProInfo, MessageType.Info); + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (GUILayout.Button("Upgrade", style)) { + Application.OpenURL(Constants.ProductPurchaseBusinessURL); + } + } + } + + internal static void RenderIndieLicenseInfo(GUIStyle style) { + string message; + if (EditorCodePatcher.licenseType == UnityLicenseType.UnityPersonalPlus) { + message = "Unity Plus users require an Indie license. Please upgrade your license on our website."; + } else if (EditorCodePatcher.licenseType == UnityLicenseType.UnityPro) { + message = "Unity Pro/Enterprise users from company with your number of employees require an Indie license. Please upgrade your license on our website."; + } else { + return; + } + GUILayout.Space(8); + using (new EditorGUILayout.HorizontalScope()) { + EditorGUILayout.HelpBox(message, MessageType.Info); + if (Event.current.type == EventType.Repaint) { + style.fixedHeight = GUILayoutUtility.GetLastRect().height; + } + if (GUILayout.Button("Upgrade", style)) { + Application.OpenURL(Constants.ProductPurchaseURL); + } + } + } + + const string GetLicense = "Get License"; + const string ContactSupport = "Contact Support"; + const string UpgradeLicense = "Upgrade License"; + const string ManageLicense = "Manage License"; + internal static Dictionary _licenseErrorData; + internal static Dictionary LicenseErrorData => _licenseErrorData ?? (_licenseErrorData = new Dictionary { + { "DeviceNotLicensedException", new LicenseErrorData(description: "Another device is using your license. Please reach out to customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport) }, + { "DeviceBlacklistedException", new LicenseErrorData(description: "You device has been blacklisted.") }, + { "DateHeaderInvalidException", new LicenseErrorData(description: $"Your license is not working because your computer's clock is incorrect. Please set the clock to the correct time to restore your license.") }, + { "DateTimeCheatingException", new LicenseErrorData(description: $"Your license is not working because your computer's clock is incorrect. Please set the clock to the correct time to restore your license.") }, + { "LicenseActivationException", new LicenseErrorData(description: "An error has occured while activating your license. Please contact customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport) }, + { "LicenseDeletedException", new LicenseErrorData(description: $"Your license has been deleted. Please contact customer support for assistance.", showBuyButton: true, buyButtonText: GetLicense, showSupportButton: true, supportButtonText: ContactSupport) }, + { "LicenseDisabledException", new LicenseErrorData(description: $"Your license has been disabled. Please contact customer support for assistance.", showBuyButton: true, buyButtonText: GetLicense, showSupportButton: true, supportButtonText: ContactSupport) }, + { "LicenseExpiredException", new LicenseErrorData(description: $"Your license has expired. Please renew your license subscription using the 'Upgrade License' button below and login with your email/password to activate your license.", showBuyButton: true, buyButtonText: UpgradeLicense, showManageLicenseButton: true, manageLicenseButtonText: ManageLicense) }, + { "LicenseInactiveException", new LicenseErrorData(description: $"Your license is currenty inactive. Please login with your email/password to activate your license.") }, + { "LocalLicenseException", new LicenseErrorData(description: $"Your license file was damaged or corrupted. Please login with your email/password to refresh your license file.") }, + // Note: obsolete + { "MissingParametersException", new LicenseErrorData(description: "An account already exists for this device. Please login with your existing email/password.", showBuyButton: true, buyButtonText: GetLicense) }, + { "NetworkException", new LicenseErrorData(description: "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists.", showSupportButton: true, supportButtonText: ContactSupport) }, + { "TrialLicenseExpiredException", new LicenseErrorData(description: $"Your trial has expired. Activate a license with unlimited usage or continue using the Free version. View available plans on our website.", showBuyButton: true, buyButtonText: UpgradeLicense) }, + { "InvalidCredentialException", new LicenseErrorData(description: "Incorrect email/password. You can find your initial password in the sign-up email.") }, + // Note: activating free trial with email is not supported anymore. This error shouldn't happen which is why we should rather user the fallback + // { "LicenseNotFoundException", new LicenseErrorData(description: "The account you're trying to access doesn't seem to exist yet. Please enter your email address to create a new account and receive a trial license.", showLoginButton: true, loginButtonText: CreateAccount) }, + { "LicenseIncompatibleException", new LicenseErrorData(description: "Please upgrade your license to continue using hotreload with Unity Pro.", showManageLicenseButton: true, manageLicenseButtonText: ManageLicense) }, + }); + internal static LicenseErrorData defaultLicenseErrorData = new LicenseErrorData(description: "We apologize, an error happened while verifying your license. Please reach out to customer support for assistance.", showSupportButton: true, supportButtonText: ContactSupport); + + internal static string GetMessageFromError(HotReloadRunTabState currentState, string error) { + if (PackageConst.IsAssetStoreBuild && error == "TrialLicenseExpiredException") { + return assetStoreProInfo; + } + return GetLicenseErrorDataOrDefault(currentState, error).description; + } + + internal static LicenseErrorData GetLicenseErrorDataOrDefault(HotReloadRunTabState currentState, string error) { + if (currentState.loginStatus?.isFree == true) { + return default(LicenseErrorData); + } + if (currentState.loginStatus == null || string.IsNullOrEmpty(error) && (!currentState.loginStatus.isLicensed || currentState.loginStatus.isTrial)) { + return new LicenseErrorData(null, showBuyButton: true, buyButtonText: GetLicense); + } + if (string.IsNullOrEmpty(error)) { + return default(LicenseErrorData); + } + if (!LicenseErrorData.ContainsKey(error)) { + return defaultLicenseErrorData; + } + return LicenseErrorData[error]; + } + + internal static void RenderBuyLicenseButton(string buyLicenseButton) { + OpenURLButton.Render(buyLicenseButton, Constants.ProductPurchaseURL); + } + + static void RenderLicenseActionButtons(HotReloadRunTabState currentState) { + var errInfo = GetLicenseErrorDataOrDefault(currentState, currentState.loginStatus?.lastLicenseError); + if (errInfo.showBuyButton || errInfo.showManageLicenseButton) { + using(new EditorGUILayout.HorizontalScope()) { + if (errInfo.showBuyButton) { + RenderBuyLicenseButton(errInfo.buyButtonText); + } + if (errInfo.showManageLicenseButton && !HotReloadPrefs.ErrorHidden) { + OpenURLButton.Render(errInfo.manageLicenseButtonText, Constants.ManageLicenseURL); + } + } + } + if (errInfo.showLoginButton && GUILayout.Button(errInfo.loginButtonText, openSettingsStyle)) { + // show license section + HotReloadWindow.Current.SelectTab(typeof(HotReloadSettingsTab)); + HotReloadWindow.Current.SettingsTab.FocusLicenseFoldout(); + } + if (errInfo.showSupportButton && !HotReloadPrefs.ErrorHidden) { + OpenURLButton.Render(errInfo.supportButtonText, Constants.ContactURL); + } + if (currentState.loginStatus?.lastLicenseError != null) { + HotReloadAboutTab.reportIssueButton.OnGUI(); + } + } + + internal static void RenderLicenseInfo(HotReloadRunTabState currentState, LoginStatusResponse loginStatus, bool verbose = false, bool allowHide = true, string overrideActionButton = null, bool showConsumptions = false) { + HotReloadPrefs.ShowLogin = EditorGUILayout.Foldout(HotReloadPrefs.ShowLogin, "Hot Reload License", true, HotReloadWindowStyles.FoldoutStyle); + if (HotReloadPrefs.ShowLogin) { + EditorGUILayout.Space(); + if ((loginStatus?.isLicensed != true && showConsumptions) && !(loginStatus == null || loginStatus.isFree)) { + RenderConsumption(loginStatus); + } + RenderLicenseStatusInfo(currentState, loginStatus: loginStatus, allowHide: allowHide, verbose: verbose); + + RenderLicenseInnerPanel(currentState, overrideActionButton: overrideActionButton); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + } + } + + internal void RenderPromoCodes() { + HotReloadPrefs.ShowPromoCodes = EditorGUILayout.Foldout(HotReloadPrefs.ShowPromoCodes, "Promo Codes", true, HotReloadWindowStyles.FoldoutStyle); + if (!HotReloadPrefs.ShowPromoCodes) { + return; + } + if (promoCodeActivatedThisSession) { + EditorGUILayout.HelpBox($"Your promo code has been successfully activated. Free trial has been extended by 3 months.", MessageType.Info); + } else { + if (promoCodeError != null && promoCodeErrorType != MessageType.None) { + EditorGUILayout.HelpBox(promoCodeError, promoCodeErrorType); + } + EditorGUILayout.LabelField("Promo code"); + _pendingPromoCode = EditorGUILayout.TextField(_pendingPromoCode); + EditorGUILayout.Space(); + + using (new EditorGUI.DisabledScope(_requestingActivatePromoCode)) { + if (GUILayout.Button("Activate promo code", HotReloadRunTab.bigButtonHeight)) { + RequestActivatePromoCode().Forget(); + } + } + } + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + } + + private async Task RequestActivatePromoCode() { + _requestingActivatePromoCode = true; + try { + var resp = await RequestHelper.RequestActivatePromoCode(_pendingPromoCode); + if (resp != null && resp.error == null) { + promoCodeActivatedThisSession = true; + } else { + var requestError = resp?.error ?? "Network error"; + var errorType = ToErrorType(requestError); + promoCodeError = ToPrettyErrorMessage(errorType); + promoCodeErrorType = ToMessageType(errorType); + } + } finally { + _requestingActivatePromoCode = false; + } + } + + PromoCodeErrorType ToErrorType(string error) { + switch (error) { + case "Input is missing": return PromoCodeErrorType.MISSING_INPUT; + case "only POST is supported": return PromoCodeErrorType.INVALID_HTTP_METHOD; + case "body is not a valid json": return PromoCodeErrorType.BODY_INVALID; + case "Promo code is not found": return PromoCodeErrorType.PROMO_CODE_NOT_FOUND; + case "Promo code already claimed": return PromoCodeErrorType.PROMO_CODE_CLAIMED; + case "Promo code expired": return PromoCodeErrorType.PROMO_CODE_EXPIRED; + case "License not found": return PromoCodeErrorType.LICENSE_NOT_FOUND; + case "License is not a trial": return PromoCodeErrorType.LICENSE_NOT_TRIAL; + case "License already extended": return PromoCodeErrorType.LICENSE_ALREADY_EXTENDED; + case "conditionalCheckFailed": return PromoCodeErrorType.CONDITIONAL_CHECK_FAILED; + } + if (error.Contains("Updating License Failed with error")) { + return PromoCodeErrorType.UPDATING_LICENSE_FAILED; + } else if (error.Contains("Unknown exception")) { + return PromoCodeErrorType.UNKNOWN_EXCEPTION; + } else if (error.Contains("Unsupported path")) { + return PromoCodeErrorType.UNSUPPORTED_PATH; + } + return PromoCodeErrorType.NONE; + } + + string ToPrettyErrorMessage(PromoCodeErrorType errorType) { + var defaultMsg = "We apologize, an error happened while activating your promo code. Please reach out to customer support for assistance."; + switch (errorType) { + case PromoCodeErrorType.MISSING_INPUT: + case PromoCodeErrorType.INVALID_HTTP_METHOD: + case PromoCodeErrorType.BODY_INVALID: + case PromoCodeErrorType.UNKNOWN_EXCEPTION: + case PromoCodeErrorType.UNSUPPORTED_PATH: + case PromoCodeErrorType.LICENSE_NOT_FOUND: + case PromoCodeErrorType.UPDATING_LICENSE_FAILED: + case PromoCodeErrorType.LICENSE_NOT_TRIAL: + return defaultMsg; + case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return "Your promo code is invalid. Please ensure that you have entered the correct promo code."; + case PromoCodeErrorType.PROMO_CODE_CLAIMED: return "Your promo code has already been used."; + case PromoCodeErrorType.PROMO_CODE_EXPIRED: return "Your promo code has expired."; + case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return "Your license has already been activated with a promo code. Only one promo code activation per license is allowed."; + case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return "We encountered an error while activating your promo code. Please try again. If the issue persists, please contact our customer support team for assistance."; + case PromoCodeErrorType.NONE: return "There is an issue connecting to our servers. Please check your internet connection or contact customer support if the issue persists."; + default: return defaultMsg; + } + } + + MessageType ToMessageType(PromoCodeErrorType errorType) { + switch (errorType) { + case PromoCodeErrorType.MISSING_INPUT: return MessageType.Error; + case PromoCodeErrorType.INVALID_HTTP_METHOD: return MessageType.Error; + case PromoCodeErrorType.BODY_INVALID: return MessageType.Error; + case PromoCodeErrorType.PROMO_CODE_NOT_FOUND: return MessageType.Warning; + case PromoCodeErrorType.PROMO_CODE_CLAIMED: return MessageType.Warning; + case PromoCodeErrorType.PROMO_CODE_EXPIRED: return MessageType.Warning; + case PromoCodeErrorType.LICENSE_NOT_FOUND: return MessageType.Error; + case PromoCodeErrorType.LICENSE_NOT_TRIAL: return MessageType.Error; + case PromoCodeErrorType.LICENSE_ALREADY_EXTENDED: return MessageType.Warning; + case PromoCodeErrorType.UPDATING_LICENSE_FAILED: return MessageType.Error; + case PromoCodeErrorType.CONDITIONAL_CHECK_FAILED: return MessageType.Error; + case PromoCodeErrorType.UNKNOWN_EXCEPTION: return MessageType.Error; + case PromoCodeErrorType.UNSUPPORTED_PATH: return MessageType.Error; + case PromoCodeErrorType.NONE: return MessageType.Error; + default: return MessageType.Error; + } + } + + public static void RenderLicenseButtons(HotReloadRunTabState currentState) { + RenderLicenseActionButtons(currentState); + } + + internal static void RenderLicenseInnerPanel(HotReloadRunTabState currentState, string overrideActionButton = null, bool renderLogout = true) { + EditorGUILayout.LabelField("Email"); + GUI.SetNextControlName("email"); + _pendingEmail = EditorGUILayout.TextField(string.IsNullOrEmpty(_pendingEmail) ? HotReloadPrefs.LicenseEmail : _pendingEmail); + _pendingEmail = _pendingEmail.Trim(); + + EditorGUILayout.LabelField("Password"); + GUI.SetNextControlName("password"); + _pendingPassword = EditorGUILayout.PasswordField(string.IsNullOrEmpty(_pendingPassword) ? HotReloadPrefs.LicensePassword : _pendingPassword); + + RenderSwitchAuthMode(); + + var e = Event.current; + using(new EditorGUI.DisabledScope(currentState.requestingLoginInfo)) { + var btnLabel = overrideActionButton; + if (String.IsNullOrEmpty(overrideActionButton)) { + btnLabel = "Login"; + } + using (new EditorGUILayout.HorizontalScope()) { + var focusedControl = GUI.GetNameOfFocusedControl(); + if (GUILayout.Button(btnLabel, bigButtonHeight) + || (focusedControl == "email" + || focusedControl == "password") + && e.type == EventType.KeyUp + && (e.keyCode == KeyCode.Return + || e.keyCode == KeyCode.KeypadEnter) + ) { + var error = ValidateEmail(_pendingEmail); + if (!string.IsNullOrEmpty(error)) { + _activateInfoMessage = new Tuple(error, MessageType.Warning); + } else if (string.IsNullOrEmpty(_pendingPassword)) { + _activateInfoMessage = new Tuple("Please enter your password.", MessageType.Warning); + } else { + HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab)); + + _activateInfoMessage = null; + if (RedeemLicenseHelper.I.RedeemStage == RedeemStage.Login) { + RedeemLicenseHelper.I.FinishRegistration(RegistrationOutcome.Indie); + } + if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) { + LoginOnDownloadAndRun(new LoginData(email: _pendingEmail, password: _pendingPassword)).Forget(); + } else { + EditorCodePatcher.RequestLogin(_pendingEmail, _pendingPassword).Forget(); + } + } + } + if (renderLogout) { + RenderLogout(currentState); + } + } + } + if (_activateInfoMessage != null && (e.type == EventType.Layout || e.type == EventType.Repaint)) { + EditorGUILayout.HelpBox(_activateInfoMessage.Item1, _activateInfoMessage.Item2); + } + } + + public static string ValidateEmail(string email) { + if (string.IsNullOrEmpty(email)) { + return "Please enter your email address."; + } else if (!EditorWindowHelper.IsValidEmailAddress(email)) { + return "Please enter a valid email address."; + } else if (email.Contains("+")) { + return "Mail extensions (in a form of 'username+suffix@example.com') are not supported yet. Please provide your original email address (such as 'username@example.com' without '+suffix' part) as we're working on resolving this issue."; + } + return null; + } + + public static void RenderLogout(HotReloadRunTabState currentState) { + if (currentState.loginStatus?.isLicensed != true) { + return; + } + if (GUILayout.Button("Logout", bigButtonHeight)) { + HotReloadWindow.Current.SelectTab(typeof(HotReloadRunTab)); + if (!EditorCodePatcher.RequestingDownloadAndRun && !EditorCodePatcher.Running) { + LogoutOnDownloadAndRun().Forget(); + } else { + RequestLogout().Forget(); + } + } + } + + async static Task LoginOnDownloadAndRun(LoginData loginData = null) { + var ok = await EditorCodePatcher.DownloadAndRun(loginData); + if (ok && loginData != null) { + HotReloadPrefs.ErrorHidden = false; + HotReloadPrefs.LicenseEmail = loginData.email; + HotReloadPrefs.LicensePassword = loginData.password; + } + } + + async static Task LogoutOnDownloadAndRun() { + var ok = await EditorCodePatcher.DownloadAndRun(); + if (!ok) { + return; + } + await RequestLogout(); + } + + private async static Task RequestLogout() { + int i = 0; + while (!EditorCodePatcher.Running && i < 100) { + await Task.Delay(100); + i++; + } + var resp = await RequestHelper.RequestLogout(); + if (!EditorCodePatcher.RequestingLoginInfo && resp != null) { + EditorCodePatcher.HandleStatus(resp); + } + } + + private static void RenderSwitchAuthMode() { + var color = EditorGUIUtility.isProSkin ? new Color32(0x3F, 0x9F, 0xFF, 0xFF) : new Color32(0x0F, 0x52, 0xD7, 0xFF); + if (HotReloadGUIHelper.LinkLabel("Forgot password?", 12, FontStyle.Normal, TextAnchor.MiddleLeft, color)) { + if (EditorUtility.DisplayDialog("Recover password", "Use company code 'naughtycult' and the email you signed up with in order to recover your account.", "Open in browser", "Cancel")) { + Application.OpenURL(Constants.ForgotPasswordURL); + } + } + } + + Texture2D _greenTextureLight; + Texture2D _greenTextureDark; + Texture2D GreenTexture => EditorGUIUtility.isProSkin + ? _greenTextureDark ? _greenTextureDark : (_greenTextureDark = MakeTexture(0.5f)) + : _greenTextureLight ? _greenTextureLight : (_greenTextureLight = MakeTexture(0.85f)); + + private void RenderProgressBar() { + if (currentState.downloadRequired && !currentState.downloadStarted) { + return; + } + + using(var scope = new EditorGUILayout.VerticalScope(HotReloadWindowStyles.MiddleCenterStyle)) { + float progress; + var bg = HotReloadWindowStyles.ProgressBarBarStyle.normal.background; + try { + HotReloadWindowStyles.ProgressBarBarStyle.normal.background = GreenTexture; + var barRect = scope.rect; + + barRect.height = 25; + if (currentState.downloadRequired) { + barRect.width = barRect.width - 65; + using (new EditorGUILayout.HorizontalScope()) { + progress = EditorCodePatcher.DownloadProgress; + EditorGUI.ProgressBar(barRect, Mathf.Clamp(progress, 0f, 1f), ""); + if (GUI.Button(new Rect(barRect) { x = barRect.x + barRect.width + 5, height = barRect.height, width = 60 }, new GUIContent(" Info", GUIHelper.GetLocalIcon("alert_info")))) { + Application.OpenURL(Constants.AdditionalContentURL); + } + } + } else { + progress = EditorCodePatcher.Stopping ? 1 : Mathf.Clamp(EditorCodePatcher.StartupProgress?.Item1 ?? 0f, 0f, 1f); + EditorGUI.ProgressBar(barRect, progress, ""); + } + GUILayout.Space(barRect.height); + } finally { + HotReloadWindowStyles.ProgressBarBarStyle.normal.background = bg; + } + } + } + + private Texture2D MakeTexture(float maxHue) { + var width = 11; + var height = 11; + Color[] pix = new Color[width * height]; + for (int y = 0; y < height; y++) { + var middle = Math.Ceiling(height / (double)2); + var maxGreen = maxHue; + var yCoord = y + 1; + var green = maxGreen - Math.Abs(yCoord - middle) * 0.02; + for (int x = 0; x < width; x++) { + pix[y * width + x] = new Color(0.1f, (float)green, 0.1f, 1.0f); + } + } + var result = new Texture2D(width, height); + result.SetPixels(pix); + result.Apply(); + return result; + } + + + /* + [MenuItem("codepatcher/restart")] + public static void TestRestart() { + CodePatcherCLI.Restart(Application.dataPath, false); + } + */ + + } + + internal static class HotReloadGUIHelper { + public static bool LinkLabel(string labelText, int fontSize, FontStyle fontStyle, TextAnchor alignment, Color? color = null) { + var stl = EditorStyles.label; + + // copy + var origSize = stl.fontSize; + var origStyle = stl.fontStyle; + var origAnchor = stl.alignment; + var origColor = stl.normal.textColor; + + // temporarily modify the built-in style + stl.fontSize = fontSize; + stl.fontStyle = fontStyle; + stl.alignment = alignment; + stl.normal.textColor = color ?? origColor; + stl.active.textColor = color ?? origColor; + stl.focused.textColor = color ?? origColor; + stl.hover.textColor = color ?? origColor; + + try { + return GUILayout.Button(labelText, stl); + } finally{ + // set the editor style (stl) back to normal + stl.fontSize = origSize; + stl.fontStyle = origStyle; + stl.alignment = origAnchor; + stl.normal.textColor = origColor; + stl.active.textColor = origColor; + stl.focused.textColor = origColor; + stl.hover.textColor = origColor; + } + } + + public static void HelpBox(string message, MessageType type, int fontSize) { + var _fontSize = EditorStyles.helpBox.fontSize; + try { + EditorStyles.helpBox.fontSize = fontSize; + EditorGUILayout.HelpBox(message, type); + } finally { + EditorStyles.helpBox.fontSize = _fontSize; + } + } + } + + internal enum PromoCodeErrorType { + NONE, + MISSING_INPUT, + INVALID_HTTP_METHOD, + BODY_INVALID, + PROMO_CODE_NOT_FOUND, + PROMO_CODE_CLAIMED, + PROMO_CODE_EXPIRED, + LICENSE_NOT_FOUND, + LICENSE_NOT_TRIAL, + LICENSE_ALREADY_EXTENDED, + UPDATING_LICENSE_FAILED, + CONDITIONAL_CHECK_FAILED, + UNKNOWN_EXCEPTION, + UNSUPPORTED_PATH, + } + + internal class LoginData { + public readonly string email; + public readonly string password; + + public LoginData(string email, string password) { + this.email = email; + this.password = password; + } + } +} + 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 000000000..051b5e6b3 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadRunTab.cs + uploadId: 668105 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 000000000..0675d9139 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs @@ -0,0 +1,697 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.Editor.Cli; +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, + "Settings", + "_Popup", + "Make changes to a build running on-device.") { + 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, "Configuration", true, HotReloadWindowStyles.FoldoutStyle); + if (HotReloadPrefs.ShowConfiguration) { + EditorGUILayout.Space(); + RenderUnityAutoRefresh(); + RenderAssetRefresh(); + if (HotReloadPrefs.AllAssetChanges) { + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderIncludeShaderChanges(); + } + + EditorGUILayout.Space(); + } + 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(); + RenderAutoRecompilePartiallyUnsupportedChanges(); + } + } + EditorGUILayout.Space(); + } + RenderConsoleWindow(); + RenderAutostart(); + + if (EditorWindowHelper.supportsNotifications) { + RenderShowNotifications(); + using (new EditorGUILayout.VerticalScope(paddedStyle ?? (paddedStyle = new GUIStyle { padding = new RectOffset(20, 0, 0, 0) }))) { + RenderShowPatchingNotifications(); + RenderShowCompilingUnsupportedNotifications(); + } + + 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(); + } + } + } + } + } + } + + void RenderUnityAutoRefresh() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent("Allow to manage Unity's Auto Compile settings (recommended)"), HotReloadPrefs.AllowDisableUnityAutoRefresh); + if (newSettings != HotReloadPrefs.AllowDisableUnityAutoRefresh) { + HotReloadPrefs.AllowDisableUnityAutoRefresh = newSettings; + } + string toggleDescription; + if (HotReloadPrefs.AllowDisableUnityAutoRefresh) { + toggleDescription = "Hot Reload will manage Unity's Auto Refresh and Script Compilation settings when it's running. Previous settings will be restored when Hot Reload is stopped."; + } else { + toggleDescription = "Enable to allow Hot Reload to manage Unity's Auto Refresh and Script Compilation settings when it's running. If enabled, previous settings will be restored when Hot Reload is stopped."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(3f); + } + + void RenderAssetRefresh() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent("Asset refresh (recommended)"), HotReloadPrefs.AllAssetChanges); + if (newSettings != HotReloadPrefs.AllAssetChanges) { + HotReloadPrefs.AllAssetChanges = newSettings; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog("Hot Reload", + $"When changing 'Asset refresh', the Hot Reload server must be restarted for this to take effect." + + "\nDo you want to restart it now?", + "Restart server", "Don't restart"); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + string toggleDescription; + if (HotReloadPrefs.AllAssetChanges) { + toggleDescription = "Hot Reload will refresh changed assets in the project."; + } else { + toggleDescription = "Enable to allow Hot Reload to refresh changed assets in the project. All asset types are supported including sprites, prefabs, shaders etc."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(3f); + } + + void RenderIncludeShaderChanges() { + HotReloadPrefs.IncludeShaderChanges = EditorGUILayout.BeginToggleGroup(new GUIContent("Refresh shaders"), HotReloadPrefs.IncludeShaderChanges); + string toggleDescription; + if (HotReloadPrefs.IncludeShaderChanges) { + toggleDescription = "Hot Reload will auto refresh shaders. Note that enabling this setting might impact performance."; + } else { + toggleDescription = "Enable to auto-refresh shaders. Note that enabling this setting might impact performance"; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderConsoleWindow() { + if (!HotReloadCli.CanOpenInBackground) { + return; + } + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent("Hide console window on start"), HotReloadPrefs.DisableConsoleWindow); + if (newSettings != HotReloadPrefs.DisableConsoleWindow) { + HotReloadPrefs.DisableConsoleWindow = newSettings; + // restart when setting changes + if (ServerHealthCheck.I.IsServerHealthy) { + var restartServer = EditorUtility.DisplayDialog("Hot Reload", + $"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?", + "Restart server", "Don't restart"); + if (restartServer) { + EditorCodePatcher.RestartCodePatcher().Forget(); + } + } + } + string toggleDescription; + if (HotReloadPrefs.DisableConsoleWindow) { + toggleDescription = "Hot Reload will start without creating a console window. Logs can be accessed through \"Help\" tab."; + } else { + toggleDescription = "Enable to start Hot Reload without creating a console window."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(3f); + } + + void RenderAutostart() { + var newSettings = EditorGUILayout.BeginToggleGroup(new GUIContent("Autostart on Unity open"), HotReloadPrefs.LaunchOnEditorStart); + if (newSettings != HotReloadPrefs.LaunchOnEditorStart) { + HotReloadPrefs.LaunchOnEditorStart = newSettings; + } + string toggleDescription; + if (HotReloadPrefs.LaunchOnEditorStart) { + toggleDescription = "Hot Reload will be launched when Unity project opens."; + } else { + toggleDescription = "Enable to launch Hot Reload when Unity project opens."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + EditorGUILayout.Space(); + } + + void RenderShowNotifications() { + GUILayout.Label("Indications", HotReloadWindowStyles.NotificationsTitleStyle); + + string toggleDescription; + if (!EditorWindowHelper.supportsNotifications && !UnitySettingsHelper.I.playmodeTintSupported) { + toggleDescription = "Indications are not supported in the Unity version you use."; + } else { + toggleDescription = "Chosen indications are enabled:"; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + } + + void RenderShowPatchingNotifications() { + HotReloadPrefs.ShowPatchingNotifications = EditorGUILayout.BeginToggleGroup(new GUIContent("Patching Indication"), HotReloadPrefs.ShowPatchingNotifications); + string toggleDescription; + if (!EditorWindowHelper.supportsNotifications) { + toggleDescription = "Patching Notification is not supported in the Unity version you use."; + } else if (!HotReloadPrefs.ShowPatchingNotifications) { + toggleDescription = "Enable to show GameView and SceneView indications when Patching."; + } else { + toggleDescription = "Indications will be shown in GameView and SceneView when Patching."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderShowCompilingUnsupportedNotifications() { + HotReloadPrefs.ShowCompilingUnsupportedNotifications = EditorGUILayout.BeginToggleGroup(new GUIContent("Compiling Unsupported Changes Indication"), HotReloadPrefs.ShowCompilingUnsupportedNotifications); + string toggleDescription; + if (!EditorWindowHelper.supportsNotifications) { + toggleDescription = "Compiling Unsupported Changes Notification is not supported in the Unity version you use."; + } else if (!HotReloadPrefs.ShowCompilingUnsupportedNotifications) { + toggleDescription = "Enable to show GameView and SceneView indications when compiling unsupported changes."; + } else { + toggleDescription = "Indications will be shown in GameView and SceneView when compiling unsupported changes."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChanges() { + HotReloadPrefs.AutoRecompileUnsupportedChanges = EditorGUILayout.BeginToggleGroup(new GUIContent("Auto recompile unsupported changes (recommended)"), HotReloadPrefs.AutoRecompileUnsupportedChanges && EditorCodePatcher.autoRecompileUnsupportedChangesSupported); + string toggleDescription; + if (!EditorCodePatcher.autoRecompileUnsupportedChangesSupported) { + toggleDescription = "Auto recompiling unsupported changes is not supported in the Unity version you use."; + } else if (HotReloadPrefs.AutoRecompileUnsupportedChanges) { + toggleDescription = "Hot Reload will recompile when unsupported changes are detected."; + } else { + toggleDescription = "Enable to recompile when unsupported changes are detected."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompilePartiallyUnsupportedChanges() { + HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges = EditorGUILayout.BeginToggleGroup(new GUIContent("Include partially unsupported changes"), HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges); + string toggleDescription; + if (HotReloadPrefs.AutoRecompilePartiallyUnsupportedChanges) { + toggleDescription = "Hot Reload will recompile partially unsupported changes."; + } else { + toggleDescription = "Enable to recompile partially unsupported changes."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesImmediately() { + HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately = EditorGUILayout.BeginToggleGroup(new GUIContent("Recompile immediately"), HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesImmediately) { + toggleDescription = "Unsupported changes will be recompiled immediately."; + } else { + toggleDescription = "Unsupported changes will be recompiled when editor is focused. Enable to recompile immediately."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesInPlayMode() { + HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode = EditorGUILayout.BeginToggleGroup(new GUIContent("Recompile in Play Mode"), HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesInPlayMode) { + toggleDescription = "Hot Reload will exit Play Mode to recompile unsupported changes."; + } else { + toggleDescription = "Enable to auto exit Play Mode to recompile unsupported changes."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderAutoRecompileUnsupportedChangesOnExitPlayMode() { + HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode = EditorGUILayout.BeginToggleGroup(new GUIContent("Recompile on exit Play Mode"), HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode); + string toggleDescription; + if (HotReloadPrefs.AutoRecompileUnsupportedChangesOnExitPlayMode) { + toggleDescription = "Hot Reload will recompile unsupported changes when exiting Play Mode."; + } else { + toggleDescription = "Enable to recompile unsupported changes when exiting Play Mode."; + } + EditorGUILayout.LabelField(toggleDescription, HotReloadWindowStyles.WrapStyle); + EditorGUILayout.EndToggleGroup(); + } + + void RenderOnDevice() { + HotReloadPrefs.ShowOnDevice = EditorGUILayout.Foldout(HotReloadPrefs.ShowOnDevice, "On-Device", 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("Make changes to a build running on-device", + 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("Manual connect", 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 = $"If auto-pair fails, find your local IP in OS settings, and use this format to connect: '{{ip}}:{RequestHelper.port}'"; + } else { + text = $"If auto-pair fails, use this IP and port to connect: {ip}:{RequestHelper.port}" + + "\nMake sure you are on the same LAN/WiFi network"; + } + GUILayout.Label(text, HotReloadWindowStyles.H3TitleWrapStyle); + + if (!currentState.isServerHealthy) { + DrawHorizontalCheck(ServerHealthCheck.I.IsServerHealthy, + "Hot Reload is running", + "Hot Reload is not running", + hasFix: false); + } + + if (!HotReloadPrefs.ExposeServerToLocalNetwork) { + var summary = $"Enable '{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("Build Settings Checklist", HotReloadWindowStyles.H3TitleStyle); + EditorGUI.BeginDisabledGroup(isSupported); + // One-click to change each setting to the supported value + if (GUILayout.Button("Fix All", 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("Options", 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: "Activate License", + 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("Switch Build Target", + "Switching the build target can take a while depending on project size.", + $"Switch to Standalone", "Cancel"); + if (confirmed) { + EditorUserBuildSettings.SwitchActiveBuildTargetAsync(BuildTargetGroup.Standalone, buildTarget); + Log.Info($"Build target is switching to {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, + $"The {selectedPlatform.ToString()} platform is selected", + $"The current platform is {selectedPlatform.ToString()} which is not supported"); + + using (new EditorGUI.DisabledScope(!isCurrentBuildTargetSupported.Value)) { + foreach (var option in allOptions) { + DrawHorizontalCheck(option.GetValue(so), + $"Enable \"{option.ShortSummary}\"", + $"Enable \"{option.ShortSummary}\""); + } + + DrawHorizontalCheck(EditorUserBuildSettings.development, + "Development Build is enabled", + "Enable \"Development Build\""); + + DrawHorizontalCheck(ScriptingBackend == ScriptingImplementation.Mono2x, + $"Scripting Backend is set to Mono", + $"Set Scripting Backend to Mono"); + + DrawHorizontalCheck(StrippingLevel == ManagedStrippingLevel.Disabled, + $"Stripping Level = {StrippingLevel}", + $"Stripping Level = {StrippingLevel}", + suggestedSolutionText: "Code stripping needs to be disabled to ensure that all methods are available for patching." + ); + } + } + + /// + /// 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 000000000..c5f3cf430 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Tabs/HotReloadSettingsTab.cs + uploadId: 668105 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 000000000..4f16b2b3c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs @@ -0,0 +1,372 @@ + +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 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; + + + 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("Window/Hot Reload &#H")] + internal static void Open() { + // opening the window on CI systems was keeping Unity open indefinitely + if (EditorWindowHelper.IsHumanControllingUs()) { + if (Current) { + Current.Show(); + Current.Focus(); + } else { + Current = GetWindow(); + } + } + } + + void OnEnable() { + 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; + } + } + + 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("Are you enjoying using Hot Reload?", MessageType.Info, 11); + if (GUILayout.Button("Hide", 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("Yes")) { + var openedUrl = PackageConst.IsAssetStoreBuild && EditorUtility.DisplayDialog("Rate Hot Reload", "Thank you for using Hot Reload!\n\nPlease consider leaving a review on the Asset Store to support us.", "Open in browser", "Cancel"); + 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("No")) { + 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($"Update To v{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("Show On Startup"); + 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; + } + } + + internal static readonly OpenURLButton autoRefreshTroubleshootingBtn = new OpenURLButton("Troubleshooting", Constants.TroubleshootingURL); + 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(); + autoRefreshTroubleshootingBtn.OnGUI(); + 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 000000000..8bd703d2e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/HotReloadWindow.cs + uploadId: 668105 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 000000000..17f2f503e --- /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 000000000..e9f95215b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/ShowOnStartupEnum.cs + uploadId: 668105 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 000000000..48002b63e --- /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 000000000..4f4ede9c1 --- /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 000000000..5911603f9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/Styles/HotReloadWindowStyles.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/LICENSE.md b/Packages/com.singularitygroup.hotreload/LICENSE.md new file mode 100644 index 000000000..b9eb62e5e --- /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 000000000..741a47def --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/LICENSE.md + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/README.md b/Packages/com.singularitygroup.hotreload/README.md new file mode 100644 index 000000000..1c3096748 --- /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 000000000..a5858a865 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/README.md + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime.meta b/Packages/com.singularitygroup.hotreload/Runtime.meta new file mode 100644 index 000000000..2b0c28248 --- /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 000000000..bad45db38 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs @@ -0,0 +1,55 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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); + } + } +} +#endif 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 000000000..815fab523 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/AppCallbackListener.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs new file mode 100644 index 000000000..c8a2d874b --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs @@ -0,0 +1,171 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + + [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; + } + } +} +#endif 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 000000000..15c30e2c4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/BuildInfo.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Burst.meta b/Packages/com.singularitygroup.hotreload/Runtime/Burst.meta new file mode 100644 index 000000000..f58389b1d --- /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 000000000..344095d8c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs @@ -0,0 +1,24 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System.Reflection; +using SingularityGroup.HotReload.DTO; + +namespace SingularityGroup.HotReload.Burst { + public static class JobHotReloadUtility { + public static void HotReloadBurstCompiledJobs(CodePatch patch, Module module) { + JobPatchUtility.PatchBurstCompiledJobs(patch, module, 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 000000000..766dc0c13 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Burst/JobHotReloadUtility.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs new file mode 100644 index 000000000..af648737e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs @@ -0,0 +1,41 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + } + } +} +#endif 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 000000000..81f2cd52e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/BurstChecker.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs new file mode 100644 index 000000000..e97a499f6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs @@ -0,0 +1,423 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) + +using System; +using System.Collections.Generic; +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 JetBrains.Annotations; +using SingularityGroup.HotReload.Burst; +using SingularityGroup.HotReload.HarmonyLib; +using SingularityGroup.HotReload.JsonConverters; +using SingularityGroup.HotReload.Newtonsoft.Json; +using SingularityGroup.HotReload.RuntimeDependencies; +using UnityEngine; +using UnityEngine.SceneManagement; + +[assembly: InternalsVisibleTo("SingularityGroup.HotReload.Editor")] + +namespace SingularityGroup.HotReload { + class RegisterPatchesResult { + public readonly List patchedMethods = new List(); + public readonly List addedMethods = new List(); + public readonly List> patchFailures = new List>(); + } + + 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; + + 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("Encountered exception when loading patches from disk:\n{0}", ex); + } + } + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void InitializeUnityEvents() { + UnityEventHelper.Initialize(); + } + + + void LoadPatches(string filePath) { + PlayerLog("Loading patches from file {0}", filePath); + var file = new FileInfo(filePath); + if(file.Exists) { + var bytes = File.ReadAllText(filePath); + var patches = JsonConvert.DeserializeObject>(bytes); + PlayerLog("Loaded {0} patches from disk", 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 RegisterPatchesResult RegisterPatches(MethodPatchResponse patches, bool persist) { + PlayerLog("Register patches.\nWarnings: {0} \nMethods:\n{1}", 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("ApplyPatches. {0} patches pending.", pendingPatches.Count); + EnsureSymbolResolver(); + + var result = new RegisterPatchesResult(); + + try { + int count = 0; + foreach(var response in pendingPatches) { + if (seenResponses.Contains(response.id)) { + continue; + } + HandleMethodPatchResponse(response, result); + patchHistory.Add(response); + + seenResponses.Add(response.id); + count += response.patches.Length; + } + if (count > 0) { + Dispatch.OnHotReload().Forget(); + } + } catch(Exception ex) { + Log.Warning("Exception occured when handling method patch. Exception:\n{0}", 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("A new Scene was loaded while new unity event methods were added at runtime. MonoBehaviours in the Scene will not trigger these new events."); + didLog = true; + }; + } + + void HandleMethodPatchResponse(MethodPatchResponse response, RegisterPatchesResult result) { + EnsureSymbolResolver(); + + foreach(var patch in response.patches) { + try { + var asm = Assembly.Load(patch.patchAssembly, patch.patchPdb); + + var module = asm.GetLoadedModules()[0]; + foreach(var sMethod in patch.newMethods) { + var newMethod = module.ResolveMethod(sMethod.metadataToken); + try { + UnityEventHelper.EnsureUnityEventMethod(newMethod); + } catch(Exception ex) { + Log.Warning("Encountered exception in EnsureUnityEventMethod: {0} {1}", ex.GetType().Name, ex.Message); + } + MethodUtils.DisableVisibilityChecks(newMethod); + if (!patch.patchMethods.Any(m => m.metadataToken == sMethod.metadataToken)) { + result.addedMethods.Add(sMethod); + } + } + + symbolResolver.AddAssembly(asm); + for (int i = 0; i < patch.modifiedMethods.Length; i++) { + var sOriginalMethod = patch.modifiedMethods[i]; + var sPatchMethod = patch.patchMethods[i]; + var err = PatchMethod(module: module, sOriginalMethod: sOriginalMethod, sPatchMethod: sPatchMethod, containsBurstJobs: patch.unityJobs.Length > 0, patchesResult: result); + if (!string.IsNullOrEmpty(err)) { + result.patchFailures.Add(Tuple.Create(sOriginalMethod, err)); + } + } + JobHotReloadUtility.HotReloadBurstCompiledJobs(patch, module); + } catch(Exception ex) { + Log.Warning("Failed to apply patch with id: {0}\n{1}", patch.patchId, ex); + } + } + } + + string PatchMethod(Module module, SMethod sOriginalMethod, SMethod sPatchMethod, bool containsBurstJobs, RegisterPatchesResult patchesResult) { + try { + var patchMethod = module.ResolveMethod(sPatchMethod.metadataToken); + var start = DateTime.UtcNow; + var state = TryResolveMethod(sOriginalMethod, patchMethod); + + if (DateTime.UtcNow - start > TimeSpan.FromMilliseconds(500)) { + Log.Info("Hot Reload apply took {0}", (DateTime.UtcNow - start).TotalMilliseconds); + } + + if(state.match == null) { + var error = + "Method mismatch: {0}, patch: {1}. This can have multiple reasons:\n" + + "1. You are running the Editor multiple times for the same project using symlinks, and are making changes from the symlink project\n" + + "2. A bug in Hot Reload. Please send us a reproduce (code before/after), and we'll get it fixed for you\n" + ; + Log.Warning(error, sOriginalMethod.simpleName, patchMethod.Name); + + return string.Format(error, sOriginalMethod.simpleName, patchMethod.Name); + } + + PlayerLog("Detour method {0:X8} {1}, offset: {2}", sOriginalMethod.metadataToken, patchMethod.Name, state.offset); + DetourResult result; + DetourApi.DetourMethod(state.match, patchMethod, out result); + if (result.success) { + patchesResult.patchedMethods.Add(sOriginalMethod); + 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 { + return HandleMethodPatchFailure(sOriginalMethod, result.exception); + } + } + } catch(Exception ex) { + 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); + if(!MethodCompatiblity.AreMethodsCompatible(resolvedMethod, patchMethod)) { + 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.genericTypeArguments, + sOriginalMethod.genericTypeArguments, + sOriginalMethod.simpleName)); + } + + string HandleMethodPatchFailure(SMethod method, Exception exception) { + var err = $"Failed to apply patch for method {method.displayName} in assembly {method.assemblyName}\n{exception}"; + Log.Warning(err); + return err; + } + + 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("Invalid path: " + filePath, nameof(filePath)); + } + Directory.CreateDirectory(dir); + var history = patchHistory.ToList(); + + PlayerLog("Saving {0} applied patches to {1}", 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(string filePath) { + seenResponses.Clear(); + var file = new FileInfo(filePath); + 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); + } + } + + + [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; + } + } + } +} +#endif 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 000000000..17d975b6b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/CodePatcher.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Demo.meta b/Packages/com.singularitygroup.hotreload/Runtime/Demo.meta new file mode 100644 index 000000000..d67b17944 --- /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 000000000..a16c17b2f --- /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 000000000..a967bee59 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity @@ -0,0 +1,1121 @@ +%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: 463b4a464af955e4d8d6b0a2923d94d0, 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: 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 &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 &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 &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_ConstrainProportionsScale: 0 + 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_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 &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 + 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: 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_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: 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_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: 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_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 &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 &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: 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_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: 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_ConstrainProportionsScale: 0 + 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_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 &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_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: 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} 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 000000000..505e36218 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBasicDemo.unity + uploadId: 668105 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 000000000..9ad180f2c --- /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 000000000..eefb3ca6b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemo.unity + uploadId: 668105 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 000000000..3c301a177 --- /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 000000000..5a510378a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scenes/HotReloadBurstDemoSettings.lighting + uploadId: 668105 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 000000000..f542db884 --- /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 000000000..724abdd5a --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs @@ -0,0 +1,179 @@ +#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; + + void Start() { + if(Application.isEditor) { + openWindowButton.onClick.AddListener(Demo.I.OpenHotReloadWindow); + openScriptButton.onClick.AddListener(() => Demo.I.OpenScriptFile(thisScript, 31, 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 = "Hot Reload is running"; + } else { + informationText.text = "Hot Reload is not running"; + } + + // // 1. 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); + + // // 1. 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; + + // // 1. 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; + // } + } + + // 2. 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; + }; + + // 3. Editing async/await methods + async Task AsyncMethod() { + // await Task.Delay(500); + // Debug.Log("AsyncMethod"); + + // // silicense warning + await Task.CompletedTask; + } + + // 4. Editing properties (get/set) + public static string SomeString { + // edit the get method + get { + var someStringHere = "This is some string"; + return someStringHere; + } + } + + // 5. 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 } + }; + + // 6. 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); + } + + void LateUpdate() { + // // 2. Editing lambda methods + // addFunction(10); + + + // // 3. Editing async/await methods + // AsyncMethod().Forget(); + + + // // 4. Editing properties (get/set) + // Debug.Log(SomeString); + + + // // 5. Editing indexers (square bracket access such as dictionaries) + // Debug.Log(randomDict["A"]); + + + // // 6. 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)); + + // // 7. 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"); + } + + // // 8. 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 000000000..493a54afc --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBasicDemo.cs + uploadId: 668105 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 000000000..b7a1fe34f --- /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 = "Hot Reload is running"; + } else { + informationText.text = "Hot Reload is not running"; + } + } + + 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 000000000..a698fa370 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/HotReloadBurstJobsDemo.cs + uploadId: 668105 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 000000000..ca2034aea --- /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 000000000..c5c4cf584 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Demo/Scripts/IDemo.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab new file mode 100644 index 000000000..85d676188 --- /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 000000000..40b417e7b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HotReloadPrompts.prefab + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs new file mode 100644 index 000000000..4a6ac9ffb --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs @@ -0,0 +1,140 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Linq; +using JetBrains.Annotations; +using System.IO; +using UnityEngine; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +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) { + #if UNITY_EDITOR + settings = AssetDatabase.LoadAssetAtPath(editorAssetPath); + #else + settings = null; + #endif + } 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() { +#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("Failed to find PromptsPrefab (are you using Hot Reload as a package?"); + } + PromptsPrefab = prefab; +#endif + } + + public void EnsurePrefabNotInBuild() { +#if UNITY_EDITOR + PromptsPrefab = null; +#endif + } + + + // put the stored settings here + + [Header("Build Settings")] + [Tooltip("Should the Hot Reload runtime be included in development builds? HotReload is never included in release builds.")] + public bool IncludeInBuild = true; + + [Header("Player Settings")] + public bool AllowAndroidAppToMakeHttpRequests = false; + + #region hidden + + /// Reference to the Prefab, for loading it at runtime + [HideInInspector] + public GameObject PromptsPrefab; + #endregion + + #endregion settings + } +} +#endif 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 000000000..c624eebb2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HotReloadSettingsObject.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs new file mode 100644 index 000000000..04cf7539d --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs @@ -0,0 +1,19 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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); + } + } + +} + +#endif \ No newline at end of file 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 000000000..33da24ccb --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/HttpClientUtils.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs new file mode 100644 index 000000000..082f41229 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs @@ -0,0 +1,11 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +namespace SingularityGroup.HotReload { + public interface IServerHealthCheck { + bool IsServerHealthy { get; } + } + + internal interface IServerHealthCheckInternal : IServerHealthCheck { + void CheckHealth(); + } +} +#endif 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 000000000..57add06bb --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/IServerHealthCheck.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs new file mode 100644 index 000000000..50eda6b65 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs @@ -0,0 +1,27 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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? + } + } +} +#endif 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 000000000..fd7e1c02d --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/InstallQRDialog.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs new file mode 100644 index 000000000..2802b46d0 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs @@ -0,0 +1,64 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +#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; + } + } +} +#endif 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 000000000..ebe4cc626 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/IpHelper.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Libs.meta b/Packages/com.singularitygroup.hotreload/Runtime/Libs.meta new file mode 100644 index 000000000..192e76259 --- /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 000000000..d961c6024 --- /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 000000000..fdea2d123 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 000000000..346fdef01 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies.dll + uploadId: 668105 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 000000000..b990749c3 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 000000000..c5490abf9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2019.dll + uploadId: 668105 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 000000000..3c6c6a7a5 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 000000000..0fc6f0002 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2020.dll + uploadId: 668105 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 000000000..11cb80c82 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 000000000..6e219de9e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/EditorOnly/SingularityGroup.HotReload.RuntimeDependencies2022.dll + uploadId: 668105 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 000000000..5c91b3fa1 --- /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 000000000..fdea2d123 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 000000000..02a3234a1 --- /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 + 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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies.dll + uploadId: 668105 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 000000000..b990749c3 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 000000000..1cb5e6b9d --- /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 + 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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2019.dll + uploadId: 668105 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 000000000..3c6c6a7a5 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 000000000..e4ad8331d --- /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 + 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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2020.dll + uploadId: 668105 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 000000000..11cb80c82 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 000000000..4ffdd5442 --- /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 + 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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Libs/OnDevice/SingularityGroup.HotReload.RuntimeDependencies2022.dll + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs new file mode 100644 index 000000000..7642daf70 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs @@ -0,0 +1,110 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) + +using System; +using System.Reflection; +using SingularityGroup.HotReload.MonoMod.Utils; + +namespace SingularityGroup.HotReload { + static class MethodCompatiblity { + internal static bool AreMethodsCompatible(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 false; + } + + static bool AreMethodBasesCompatible(MethodBase previousMethod, MethodBase patchMethod) { + if(previousMethod.Name != patchMethod.Name) { + return false; + } + //Declaring type of patch method is different from the target method but their full name (namespace + name) is equal + if(previousMethod.DeclaringType.FullName != patchMethod.DeclaringType.FullName) { + return false; + } + //Check in case type parameter overloads to distinguish between: void M() { } <-> void M() { } + if(previousMethod.IsGenericMethodDefinition != patchMethod.IsGenericMethodDefinition) { + return false; + } + + 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 false; + } + //this parameter has to be the declaring type + if(!ParamTypeMatches(patchParams[0].ParameterType, previousMethod.DeclaringType)) { + return false; + } + //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; + } + if (prevParams.Length >= 1 && prevParams[0].ParameterType == 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 bool CompareParameters(ArraySegment x, ArraySegment y) { + if(x.Count != y.Count) { + return false; + } + for (var i = 0; i < x.Count; i++) { + if(x.Array[i + x.Offset].ParameterType != y.Array[i + y.Offset].ParameterType) { + return false; + } + } + return true; + } + + + static bool AreConstructorsCompatible(ConstructorInfo x, ConstructorInfo y) { + return AreMethodBasesCompatible(x, y); + } + + static bool AreMethodInfosCompatible(MethodInfo x, MethodInfo y) { + return AreMethodBasesCompatible(x, y) && x.ReturnType == y.ReturnType; + } + } +} +#endif 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 000000000..f4f81366a --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MethodCompatiblity.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs new file mode 100644 index 000000000..55d56b169 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs @@ -0,0 +1,500 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) + +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; + + 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; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new MethodPatchResponse( + id ?? string.Empty, + patches ?? Array.Empty(), + failures ?? Array.Empty(), + removedMethod ?? Array.Empty(), + // Note: doesn't have to be persisted here + 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; + + 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; + + 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() + )); + } + + 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 SMethod ReadSMethod(JsonReader reader) { + string assemblyName = null; + string displayName = null; + int metadataToken = default(int); + SType[] genericTypeArguments = null; + SType[] genericArguments = null; + 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.genericTypeArguments): + genericTypeArguments = ReadSTypeArray(reader); + break; + + case nameof(SMethod.genericArguments): + genericArguments = ReadSTypeArray(reader); + 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, + genericTypeArguments ?? Array.Empty(), + genericArguments ?? Array.Empty(), + simpleName ?? string.Empty + ); + } + + private SType ReadSType(JsonReader reader) { + string assemblyName = null; + string typeName = null; + SType[] genericArguments = 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(SType.assemblyName): + assemblyName = reader.ReadAsString(); + break; + + case nameof(SType.typeName): + typeName = reader.ReadAsString(); + break; + + case nameof(SType.genericArguments): + genericArguments = ReadSTypeArray(reader); + break; + + default: + reader.Skip(); // Skip unknown properties + break; + } + } + + return new SType( + assemblyName ?? string.Empty, + typeName ?? string.Empty, + genericArguments ?? Array.Empty() + ); + } + + 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); + } + + 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(); + } + + 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(); + } + + 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); + + if (method.genericTypeArguments != null) { + writer.WritePropertyName(nameof(method.genericTypeArguments)); + writer.WriteStartArray(); + foreach (var genericTypeArgument in method.genericTypeArguments) { + WriteSType(writer, genericTypeArgument); + } + writer.WriteEndArray(); + } + + if (method.genericArguments != null) { + writer.WritePropertyName(nameof(method.genericArguments)); + writer.WriteStartArray(); + foreach (var genericArgument in method.genericArguments) { + WriteSType(writer, genericArgument); + } + writer.WriteEndArray(); + } + + writer.WritePropertyName(nameof(method.simpleName)); + writer.WriteValue(method.simpleName); + + writer.WriteEndObject(); + } + + void WriteSType(JsonWriter writer, SType type) { + writer.WriteStartObject(); + + writer.WritePropertyName(nameof(type.assemblyName)); + writer.WriteValue(type.assemblyName); + writer.WritePropertyName(nameof(type.typeName)); + writer.WriteValue(type.typeName); + + // always writing generic arguments will cause recursion issues + if (type.genericArguments?.Length > 0) { + writer.WritePropertyName(nameof(type.genericArguments)); + writer.WriteStartArray(); + foreach (var genericArgument in type.genericArguments) { + WriteSType(writer, genericArgument); + } + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } + } +} +#endif 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 000000000..d1e583d50 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MethodPatchResponsesConverter.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs new file mode 100644 index 000000000..14e33d7c4 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs @@ -0,0 +1,22 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + } + } +#else + public static void DisableVisibilityChecks(MethodBase method) { } +#endif + } +} +#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 000000000..3e9cb1d05 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MethodUtils.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours.meta b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours.meta new file mode 100644 index 000000000..fa0e4ef0f --- /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 000000000..26bbbd523 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs @@ -0,0 +1,80 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + internal class ConnectionDialog : MonoBehaviour { + [Header("UI controls")] + public Button buttonHide; + + [Header("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 = ConnectionSummary.Connected) { + 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 = $"Patches: {pendingPatches} pending, {patchesApplied} applied"; + } + } + + /// 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 const string Cancelled = "Cancelled"; + public const string Connecting = "Connecting ..."; + public const string Handshaking = "Handshaking ..."; + public const string DifferencesFound = "Differences found"; + public const string Connected = "Connected!"; + // reconnecting can be shown for a long time, so a longer message is okay + public const string TryingToReconnect = "Trying to reconnect ..."; + public const string Disconnected = "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 000000000..7dc0b13f7 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/ConnectionDialog.cs + uploadId: 668105 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 000000000..ea9a4985e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs @@ -0,0 +1,134 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Collections; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.EventSystems; + +namespace SingularityGroup.HotReload { + internal class Prompts : MonoBehaviour { + public GameObject retryPrompt; + public GameObject connectedPrompt; + public GameObject questionPrompt; + + [Header("Other")] + [Tooltip("Used when project does not create an EventSystem early enough")] + 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("IsPlayerWithHotReload() is false"); + } + 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 (Input.touchCount > 0 || Input.GetMouseButtonDown(0)) { + userTriedToInteract = true; + DoEnsureEventSystem(); + } + } + } + + 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($"No EventSystem is active, enabling an EventSystem inside Hot Reload {name} prefab." + + " A Unity EventSystem and an Input module is required for tapping buttons on the Unity UI."); + 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 000000000..3c63322ec --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/Prompts.cs + uploadId: 668105 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 000000000..fef457735 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs @@ -0,0 +1,64 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + class QuestionDialog : MonoBehaviour { + + [Header("Information")] + public Text textSummary; + public Text textSuggestion; + + [Header("UI controls")] + 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 = "Continue"; + public string cancelButtonText = "Cancel"; + public string moreInfoUrl = "https://hotreload.net/documentation#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 000000000..df0724a79 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/QuestionDialog.cs + uploadId: 668105 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 000000000..204de6ab3 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs @@ -0,0 +1,104 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.UI; + +namespace SingularityGroup.HotReload { + internal class RetryDialog : MonoBehaviour { + [Header("UI controls")] + 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(() => { + Application.OpenURL("https://hotreload.net/documentation#connection-issues"); + }); + } + + [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 = $"Is this device connected to {networkText}?"; + var waitForCompiling = "Wait for compiling to finish before trying again"; + var targetNetworkIsReachable = $"Make sure you're on the same {networkText} network. Also ensure Hot Reload is running"; + + 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 ? "Auto-pair encountered an issue" : "Connection failed"; + + 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 000000000..7a558ba76 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoBehaviours/RetryDialog.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs new file mode 100644 index 000000000..abe609e2e --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs @@ -0,0 +1,196 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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 + } +} +#endif 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 000000000..7e12c4879 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/MonoMethod.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OSX.meta b/Packages/com.singularitygroup.hotreload/Runtime/OSX.meta new file mode 100644 index 000000000..8a2fdb5fb --- /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 000000000..b63141af6 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 000000000..23bfe2500 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/OSX/HotReloadNativeHelper.dylib + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs new file mode 100644 index 000000000..c17f3af16 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs @@ -0,0 +1,187 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +#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; +#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) + // Currently, we don't support [InvokeOnHotReload] on patched methods + public static async Task OnHotReload() { + var methods = await Task.Run(() => GetOrFillMethodsCacheThreaded()); + + foreach (var m in methods) { + if (m.IsStatic) { + InvokeStaticMethod(m, nameof(InvokeOnHotReload)); + } else { + foreach (var go in GameObject.FindObjectsOfType(m.DeclaringType)) { + InvokeInstanceMethod(m, go); + } + } + } + } + + 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)); + } 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} failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour."); + } + } + + 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)}] failed to find method {attrib.methodToInvoke}. Make sure it exists within the same class."); + return; + } + if (reloadMethod.IsStatic) { + InvokeStaticMethod(reloadMethod, nameof(InvokeOnHotReloadLocal)); + } else if (typeof(MonoBehaviour).IsAssignableFrom(reloadForType)) { + foreach (var go in GameObject.FindObjectsOfType(reloadForType)) { + InvokeInstanceMethod(reloadMethod, go); + } + } else { + Log.Warning($"[{nameof(InvokeOnHotReloadLocal)}] {reloadMethod.DeclaringType?.Name} {reloadMethod.Name} failed. Make sure it's a method with 0 parameters either static or defined on MonoBehaviour."); + } + } + + 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) { + try { + m.Invoke(null, new object[] { }); + } catch (Exception e) { + if (m.GetParameters().Length != 0) { + Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} failed. Make sure it has 0 parameters. Exception:\n{e}"); + } else { + Log.Warning($"[{attrName}] {m.DeclaringType?.Name} {m.Name} failed. Exception\n{e}"); + } + } + } + + private static void InvokeInstanceMethod(MethodBase m, Object go) { + try { + m.Invoke(go, new object[] { }); + } catch (Exception e) { + if (m.GetParameters().Length != 0) { + Log.Warning($"[InvokeOnHotReload] {m.DeclaringType?.Name} {m.Name} failed. Make sure it has 0 parameters. Exception:\n{e}"); + } else { + Log.Warning($"[InvokeOnHotReload] {m.DeclaringType?.Name} {m.Name} failed. Exception:\n{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} failed. Make sure it has 0 parameters. Exception:\n{e}"); + } else { + Log.Warning($"[InvokeOnHotReloadLocal] {m.DeclaringType?.Name} {m.Name} failed. Exception:\n{e}"); + } + } + } + + } +} +#endif 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 000000000..8fc54808b --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/OnHotReloadDispatch.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs new file mode 100644 index 000000000..46a3c1f75 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs @@ -0,0 +1,20 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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.12.10"; + // Never higher than Version + // Used for the download + public const string ServerVersion = "1.12.10"; + public const string PackageName = "com.singularitygroup.hotreload"; + public const string LibraryCachePath = "Library/" + PackageName; + public const string ConfigFileName = "hot-reload-config.json"; + } +} +#endif 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 000000000..6642dd450 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PackageConst.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs new file mode 100644 index 000000000..07e6db7d8 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs @@ -0,0 +1,41 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + } + } +} +#endif 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 000000000..c46f76360 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PatchServerInfo.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs new file mode 100644 index 000000000..8df27f529 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs @@ -0,0 +1,15 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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"); + } + } +} +#endif 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 000000000..30bb7fdc7 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PersistencePaths.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs new file mode 100644 index 000000000..d12396a15 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs @@ -0,0 +1,118 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Threading; +using System.Threading.Tasks; +using SingularityGroup.HotReload.DTO; + +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("Hot Reload was unreachable for 5 seconds, trying to reconnect..."); + // 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($"Hot Reload was unreachable for {reconnectTimeout} seconds, disconnecting"); + 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); + // TODO handle new response data (removed methods etc.) + 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; + } + } + +} +#endif 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 000000000..b139c4b07 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PlayerCodePatcher.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs new file mode 100644 index 000000000..80c7587d2 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs @@ -0,0 +1,159 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +#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; + +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(); + bool onlyPrefabMissing; + if (!IsPlayerWithHotReload(out onlyPrefabMissing)) { + if (onlyPrefabMissing) { + Log.Warning("Hot Reload is not available in this build because one or more build settings were not supported."); + } + return; + } + + TryAutoConnect().Forget(); + } + + static async Task TryAutoConnect() { + try { + buildInfo = await GetBuildInfo(); + } catch (Exception e) { + if (e is IOException) { + Log.Warning("Hot Reload is not available in this build because one or more build settings were not supported."); + } else { + Log.Error($"Uknown exception happened when reading build info\n{e.GetType().Name}: {e.Message}"); + } + return; + } + if (buildInfo == null) { + Log.Error($"Uknown issue happened when reading build info."); + return; + } + + 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("Build info not found"); + } + 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($"Server is healthy after first handshake? {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 + } + } + } +} +#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 000000000..8c3168b02 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/PlayerEntrypoint.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/Public.meta b/Packages/com.singularitygroup.hotreload/Runtime/Public.meta new file mode 100644 index 000000000..d48c86f1e --- /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 000000000..009fdd58d --- /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 000000000..0fe8ef3a5 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/HotReloadLogging.cs + uploadId: 668105 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 000000000..23b285cbd --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs @@ -0,0 +1,17 @@ +using System; + +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 { + } + +} 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 000000000..9b14ba073 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReload.cs + uploadId: 668105 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 000000000..4fa5ecfe2 --- /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 000000000..1590e51be --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/InvokeOnHotReloadLocal.cs + uploadId: 668105 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 000000000..d790311be --- /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 000000000..fd4a533dd --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/Log.cs + uploadId: 668105 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 000000000..e062951cb --- /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 000000000..83278e2d6 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/LogLevel.cs + uploadId: 668105 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 000000000..834ee8f19 --- /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 000000000..4b16945d9 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/Public/SingularityGroup.HotReload.Runtime.Public.asmdef + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs new file mode 100644 index 000000000..c513615f7 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs @@ -0,0 +1,418 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Collections.Generic; +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.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 = "https://d2tc55zjhw51ly.cloudfront.net/releases/latest/changelog.json"; + static readonly string defaultOrigin = Path.GetDirectoryName(UnityHelper.DataPath); + public static string origin { get; private set; } = defaultOrigin; + + 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", Path.GetDirectoryName(UnityHelper.DataPath)); + + return httpClient; + } + + /// + /// Create url for a hostname and port + /// + internal static string CreateUrl(PatchServerInfo server) { + return $"http://{server.hostName}:{server.port.ToString()}"; + } + + public static void SetServerPort(int port) { + serverInfo = new PatchServerInfo(serverInfo.hostName, port, serverInfo.commitHash, serverInfo.rootPath); + cachedUrl = null; + Log.Debug($"SetServerInfo to {CreateUrl(serverInfo)}"); + } + + public static void SetServerInfo(PatchServerInfo info) { + if (info != null) Log.Debug($"SetServerInfo to {CreateUrl(info)}"); + serverInfo = info; + cachedUrl = null; + + if (info?.customRequestOrigin != null) { + SetOrigin(info.customRequestOrigin); + } + } + + // 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; + var searchPaths = assemblySearchPaths ?? CodePatcher.I.GetAssemblySearchPaths(); + var body = SerializeRequestBody(new MethodPatchRequest(lastPatchId, searchPaths, TimeSpan.FromSeconds(20), Path.GetDirectoryName(Application.dataPath))); + + 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("PollMethodPatches failed with code {0} {1} {2}", (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("PollPatchStatus failed with code {0} {1} {2}", (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("PollAssetChanges failed with code {0} {1} {2}", (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 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($"Deserializing response failed with {ex.GetType().Name}: {ex.Message}"); + } + } else { + return LoginStatusResponse.FromRequestError(resp.responseText ?? "Request timeout"); + } + } + + 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) { + 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 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 Task RequestClearPatches() { + var body = SerializeRequestBody(new CompileRequest(serverInfo.rootPath)); + return PostJson(url + "/clearpatches", body, 10); + } + + public static Task RequestCompile() { + var body = SerializeRequestBody(new CompileRequest(serverInfo.rootPath)); + return PostJson(url + "/compile", body, 10); + } + + 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); + } + } + } +} +#endif 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 000000000..d8e809fb2 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/RequestHelper.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs new file mode 100644 index 000000000..769bcf808 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs @@ -0,0 +1,245 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +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 = "Hot Reload was started from a different project", + suggestion = "Please run Hot Reload from the matching Unity project", + 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 = "Editor and current build are on different commits", + suggestion = "This can cause errors when the build was made on an old commit.", + 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("Request handshake to Hot Reload server with hostname: {0}", 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("Server did not declare its current Unity activeBuildTarget in the handshake response. Will assume it is {0}.", buildInfo.activeBuildTarget); + results |= Result.QuietWarning; + } else if (remoteBuildTarget != buildInfo.activeBuildTarget) { + Log.Warning("Your Unity project is running on {0}. You may need to switch it to {1} for Hot Reload to work.", remoteBuildTarget, buildInfo.activeBuildTarget); + results |= Result.QuietWarning; + } + + if (verified) { + results |= Result.Verified; + } + return results; + } + } +} +#endif 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 000000000..7743c2c99 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ServerHandshake.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs new file mode 100644 index 000000000..a1f8d2b65 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs @@ -0,0 +1,58 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + } + } +} +#endif 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 000000000..3b1c2acf4 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ServerHealthCheck.cs + uploadId: 668105 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 000000000..895e5c377 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef @@ -0,0 +1,27 @@ +{ + "name": "SingularityGroup.HotReload.Runtime", + "rootNamespace": "SingularityGroup.HotReload", + "references": [ + "GUID:343deaaf83e0cee4ca978e7df0b80d21", + "GUID:2bafac87e7f4b9b418d9448d219b01ab", + "GUID:46c537318f0d530469a1df1fafe86c9c", + "GUID:af67dfb2ec5c9c740be50ae4470ed85f", + "SingularityGroup.HotReload.Runtime.Public" + ], + "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" + ], + "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 000000000..2e33baedd --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/SingularityGroup.HotReload.Runtime.asmdef + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs new file mode 100644 index 000000000..885b826ea --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs @@ -0,0 +1,90 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) + +using System; +using System.Collections.Generic; +using System.Reflection; +using SingularityGroup.HotReload.DTO; +using SingularityGroup.HotReload.RuntimeDependencies; + +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) { + List assemblies; + if (assembliesByName.TryGetValue(t.assemblyName, out assemblies)) { + + Type type; + foreach (var assembly in assemblies) { + if ((type = assembly.GetType(t.typeName)) != null) { + if(t.typeName == "System.Array" && t.genericArguments.Length > 0) { + var elementType = Resolve(t.genericArguments[0]); + return elementType.Assembly.GetType(t.genericArguments[0].typeName + "[]"); + } + if(t.genericArguments.Length > 0) { + type = type.MakeGenericType(ResolveTypes(t.genericArguments)); + } + return type; + } + } + } + throw new SymbolResolvingFailedException(t); + } + + 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); + var genericTypeArgs = ResolveTypes(m.genericTypeArguments); + var genericMethodArgs = ResolveTypes(m.genericArguments); + MethodBase result = null; + Exception lastException = null; + for (var i = 0; i < assmeblies.Count; i++) { + try { + result = assmeblies[i].GetLoadedModules()[0].ResolveMethod(m.metadataToken, genericTypeArgs, genericMethodArgs); + break; + } catch(Exception ex) { + lastException = ex; + } + } + if(result == null) { + throw new SymbolResolvingFailedException(m, lastException); + } + return result; + } + + Type[] ResolveTypes(SType[] sTypes) { + if(sTypes == null) { + return null; + } + if(sTypes.Length == 0) { + return Array.Empty(); + } + var result = new Type[sTypes.Length]; + for (int i = 0; i < sTypes.Length; i++) { + result[i] = Resolve(sTypes[i]); + } + return result; + } + } +} +#endif 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 000000000..620fe2eab --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/SymbolResolver.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs new file mode 100644 index 000000000..3dabcb411 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs @@ -0,0 +1,14 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using SingularityGroup.HotReload.DTO; + +namespace SingularityGroup.HotReload { + internal class SymbolResolvingFailedException : Exception { + public SymbolResolvingFailedException(SMethod m, Exception inner) + : base($"Unable to resolve method {m.displayName} in assembly {m.assemblyName}", inner) { } + + public SymbolResolvingFailedException(SType t) + : base($"Unable to resolve type with name: {t.typeName} in assembly {t.assemblyName}") { } + } +} +#endif 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 000000000..a71b07a1d --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/SymbolResolvingFailedException.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs new file mode 100644 index 000000000..9056da270 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs @@ -0,0 +1,48 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +using System; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +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("unknown exception " + 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; + } + } +} +#endif 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 000000000..6bf28db1e --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/TaskExtensions.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs new file mode 100644 index 000000000..d38e64bec --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs @@ -0,0 +1,226 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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(); + } + } + } +} +#endif 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 000000000..33560e10c --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs new file mode 100644 index 000000000..4c04325d6 --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs @@ -0,0 +1,42 @@ +#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR) +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; + } + } +} +#endif 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 000000000..49bc19f85 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Runtime/UnityHelper.cs + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/Server.meta b/Packages/com.singularitygroup.hotreload/Server.meta new file mode 100644 index 000000000..37030251c --- /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 000000000..41674e3aa --- /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 000000000..3fa6e9104 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Server/hot-reload-config.graphql + uploadId: 668105 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 000000000..db93a9157 --- /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 000000000..d218d47ff --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh @@ -0,0 +1,86 @@ +#!/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" + +# Needs be removed if you have multiple unities +pgrep CodePatcherCLI | xargs -I {} kill {} + +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 000000000..d47efc165 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Server/linux-x64/hotreload-start-script.sh + uploadId: 668105 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 000000000..39fee8df7 --- /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 000000000..687274d63 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Server/linux-x64/terminal-run.sh + uploadId: 668105 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 000000000..991b85600 --- /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 000000000..e90d92024 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/Third Party Notices.md + uploadId: 668105 diff --git a/Packages/com.singularitygroup.hotreload/package.json b/Packages/com.singularitygroup.hotreload/package.json new file mode 100644 index 000000000..47094d82c --- /dev/null +++ b/Packages/com.singularitygroup.hotreload/package.json @@ -0,0 +1,20 @@ +{ + "displayName": "Hot Reload", + "name": "com.singularitygroup.hotreload", + "unity": "2018.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.12.10", + "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 000000000..d2a6fb4d0 --- /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.12.10 + assetPath: Packages/com.singularitygroup.hotreload/package.json + uploadId: 668105 diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 7d4d4d184..46a3c35af 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -87,6 +87,15 @@ "dependencies": {}, "hash": "e74a84218512a229aac65488404a7a412cb86fa4" }, + "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.2d.animation": { "version": "10.1.1", "depth": 1,