[Add] Hot Reload
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
|
||||
#pragma warning disable CS0618
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
public class BuildGenerateBuildInfo : IPreprocessBuild, IPostprocessBuild {
|
||||
public int callbackOrder => 10;
|
||||
|
||||
public void OnPreprocessBuild(BuildTarget target, string path) {
|
||||
try {
|
||||
if (!HotReloadBuildHelper.IncludeInThisBuild() || MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
// write BuildInfo json into the StreamingAssets directory
|
||||
GenerateBuildInfo(BuildInfo.GetStoredPath(), target);
|
||||
} catch (BuildFailedException) {
|
||||
throw;
|
||||
} catch (Exception e) {
|
||||
throw new BuildFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateBuildInfo(string buildFilePath, BuildTarget buildTarget) {
|
||||
var buildInfo = BuildInfoHelper.GenerateBuildInfoMainThread(buildTarget);
|
||||
// write to StreamingAssets
|
||||
// create StreamingAssets folder if not exists (in-case project has no StreamingAssets files)
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(buildFilePath));
|
||||
File.WriteAllText(buildFilePath, buildInfo.ToJson());
|
||||
}
|
||||
|
||||
public void OnPostprocessBuild(BuildTarget target, string path) {
|
||||
try {
|
||||
File.Delete(BuildInfo.GetStoredPath());
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 178df48ca88b4cddac448a49196b49bf
|
||||
timeCreated: 1682338738
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/BuildGenerateBuildInfo.cs
|
||||
uploadId: 870414
|
||||
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal static class HotReloadBuildHelper {
|
||||
/// <summary>
|
||||
/// Should HotReload runtime be included in the current build?
|
||||
/// </summary>
|
||||
public static bool IncludeInThisBuild() {
|
||||
return IsAllBuildSettingsSupported();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get scripting backend for the current platform.
|
||||
/// </summary>
|
||||
/// <returns>Scripting backend</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Some platforms are not supported because they don't have Mono scripting backend.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only list the platforms that definately don't have Mono scripting.
|
||||
/// </remarks>
|
||||
private static readonly BuildTargetGroup[] unsupportedBuildTargets = new [] {
|
||||
BuildTargetGroup.iOS, // mono support was removed many years ago
|
||||
BuildTargetGroup.WebGL, // has never had mono
|
||||
};
|
||||
|
||||
#pragma warning disable CS0618
|
||||
public static bool IsMonoSupported(BuildTargetGroup buildTarget) {
|
||||
var backend = PlayerSettings.GetScriptingBackend(buildTarget);
|
||||
try {
|
||||
// GetDefaultScriptingBackend returns IL2CPP for Unity 6 which goes against Unity documentation.
|
||||
// Have to use a workaround approach instead
|
||||
PlayerSettings.SetScriptingBackend(buildTarget, ScriptingImplementation.Mono2x);
|
||||
return PlayerSettings.GetScriptingBackend(buildTarget) == ScriptingImplementation.Mono2x;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
PlayerSettings.SetScriptingBackend(buildTarget, backend);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9aa611f02544b609c5b29f9d1409d6e
|
||||
timeCreated: 1674041425
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/HotReloadBuildHelper.cs
|
||||
uploadId: 870414
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
using UnityEditor.Android;
|
||||
using UnityEditor.Build;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
#pragma warning disable CS0618
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This class sets option in the AndroidManifest that you choose in HotReload build settings.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// - To connect to the HotReload server through the local network, we need to permit access to http://192...<br/>
|
||||
/// - Starting with Android 9, insecure http requests are not allowed by default and must be whitelisted
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal class PostbuildModifyAndroidManifest : IPostGenerateGradleAndroidProject {
|
||||
#pragma warning restore CS0618
|
||||
public int callbackOrder => 10;
|
||||
|
||||
private const string manifestFileName = "AndroidManifest.xml";
|
||||
|
||||
public void OnPostGenerateGradleAndroidProject(string path) {
|
||||
try {
|
||||
if (!HotReloadBuildHelper.IncludeInThisBuild()) {
|
||||
return;
|
||||
}
|
||||
// Note: in future we may support users with custom configuration for usesCleartextTraffic
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
// Unity 2022 or newer → do nothing, we rely on Unity option to control the flag
|
||||
#else
|
||||
// Unity 2021 or older → put manifest flag in if Unity is making a Development Build
|
||||
var manifestFilePath = FindAndroidManifest(path);
|
||||
if (manifestFilePath == null) {
|
||||
throw new BuildFailedException(string.Format(Translations.Errors.ExceptionUnableToFindManifest, CodePatcher.TAG, manifestFileName));
|
||||
}
|
||||
SetUsesCleartextTraffic(manifestFilePath);
|
||||
#endif
|
||||
} catch (BuildFailedException) {
|
||||
throw;
|
||||
} catch (Exception e) {
|
||||
throw new BuildFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// identifier that is used in the deeplink uri scheme
|
||||
/// (initially tried Application.identifier, but that was giving unexpected results based on PlayerSettings)
|
||||
// SG-29580
|
||||
// Something to uniqly identify the application, but it must be something which is highly likely
|
||||
// to be the same at build time (studio might have logic to set e.g. product name to MyGameProd or MyGameTest)
|
||||
public static string ApplicationIdentiferSlug => "app";
|
||||
/*
|
||||
public static string ApplicationIdentiferSlug => Regex.Replace(ApplicationIdentifer, @"[^a-zA-Z0-9\.\-]", "")
|
||||
.Replace("..", ".") // happens if your companyname in Unity ends with a dot
|
||||
.ToLowerInvariant();
|
||||
|
||||
private static void AddDeeplinkForwarder(string manifestFilePath) {
|
||||
// add the hotreload-${identifier} uri scheme to the AndroidManifest.xml file
|
||||
// it should be added as part of an intent-filter for the activity "com.singularitygroup.deeplinkforwarder.DeepLinkForwarderActivity"
|
||||
var contents = File.ReadAllText(manifestFilePath);
|
||||
if (contents.Contains("android:name=\"com.singularitygroup.deeplinkforwarder.DeepLinkForwarderActivity\"")) {
|
||||
// user has already set this themselves, don't replace it
|
||||
return;
|
||||
}
|
||||
|
||||
//note: not using android:host or any other data attr because android still shows a chooser for all ur hotreload apps
|
||||
// Therefore must use a unique uri scheme to ensure only one app can handle it.
|
||||
var activityWithIntentFilter = @"
|
||||
<activity android:name=""com.singularitygroup.deeplinkforwarder.DeepLinkForwarderActivity"">
|
||||
<intent-filter>
|
||||
<action android:name=""android.intent.action.VIEW"" />
|
||||
<category android:name=""android.intent.category.DEFAULT"" />
|
||||
<category android:name=""android.intent.category.BROWSABLE"" />
|
||||
<data android:scheme=""hotreload-" + ApplicationIdentiferSlug + @""" />
|
||||
</intent-filter>
|
||||
</activity>";
|
||||
var newContents = Regex.Replace(contents,
|
||||
@"</application>",
|
||||
activityWithIntentFilter + "\n </application>"
|
||||
);
|
||||
File.WriteAllText(manifestFilePath, newContents);
|
||||
}
|
||||
*/
|
||||
// Assume unityLibraryPath is to {gradleProject}/unityLibrary/ which is roughly the same across Unity versions 2018/2019/2020/2021/2022
|
||||
private static string FindAndroidManifest(string unityLibraryPath) {
|
||||
// find the AndroidManifest.xml file which we can edit
|
||||
var dir = new DirectoryInfo(unityLibraryPath);
|
||||
var manifestFilePath = Path.Combine(dir.FullName, "src", "main", manifestFileName);
|
||||
if (File.Exists(manifestFilePath)) {
|
||||
return manifestFilePath;
|
||||
}
|
||||
|
||||
Log.Info(Translations.Errors.InfoManifestSearch, manifestFileName, manifestFilePath, dir.FullName);
|
||||
var manifestFiles = dir.GetFiles(manifestFileName, SearchOption.AllDirectories);
|
||||
if (manifestFiles.Length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var file in manifestFiles) {
|
||||
if (file.FullName.Contains("src")) {
|
||||
// good choice
|
||||
return file.FullName;
|
||||
}
|
||||
}
|
||||
// fallback to the first file found
|
||||
return manifestFiles[0].FullName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set option android:usesCleartextTraffic="true"
|
||||
|
||||
/// </summary>
|
||||
/// <param name="manifestFilePath">Absolute filepath to the unityLibrary AndroidManifest.xml file</param>
|
||||
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,
|
||||
@"<application\s",
|
||||
"<application android:usesCleartextTraffic=\"true\" "
|
||||
);
|
||||
newContents += $"\n<!-- {string.Format(Translations.Errors.CommentAndroidCleartextPermit, CodePatcher.TAG)} -->";
|
||||
newContents += $"\n<!-- {string.Format(Translations.Errors.CommentAndroidCleartextDevelopmentOnly, CodePatcher.TAG)} -->";
|
||||
File.WriteAllText(manifestFilePath, newContents);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1949292efc07445ea4c040d544e2d369
|
||||
timeCreated: 1675441886
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildModifyAndroidManifest.cs
|
||||
uploadId: 870414
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using SingularityGroup.HotReload.Editor.Cli;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
#pragma warning disable CS0618
|
||||
class PostbuildSendProjectState : IPostprocessBuild {
|
||||
#pragma warning restore CS0618
|
||||
public int callbackOrder => 9999;
|
||||
public void OnPostprocessBuild(BuildTarget target, string path) {
|
||||
try {
|
||||
if (!HotReloadBuildHelper.IncludeInThisBuild() || MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
// after build passes, need to send again because EditorApplication.delayCall isn't called.
|
||||
var buildInfo = BuildInfoHelper.GenerateBuildInfoMainThread();
|
||||
HotReloadCli.PrepareBuildInfo(buildInfo);
|
||||
} catch (BuildFailedException) {
|
||||
throw;
|
||||
} catch (Exception e) {
|
||||
throw new BuildFailedException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b27b9eab16f78f448477e546fd5eb97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PostbuildSendProjectState.cs
|
||||
uploadId: 870414
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
using SingularityGroup.HotReload.Editor.Localization;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
/// <summary>Includes HotReload Resources only in development builds</summary>
|
||||
/// <remarks>
|
||||
/// This build script ensures that HotReload Resources are not included in release builds.
|
||||
/// <para>
|
||||
/// When HotReload is enabled:<br/>
|
||||
/// - include HotReloadSettingsObject in development Android builds.<br/>
|
||||
/// - exclude HotReloadSettingsObject from the build.<br/>
|
||||
/// When HotReload is disabled:<br/>
|
||||
/// - excludes HotReloadSettingsObject from the build.<br/>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
#pragma warning disable CS0618
|
||||
internal class PrebuildIncludeResources : IPreprocessBuild, IPostprocessBuild {
|
||||
#pragma warning restore CS0618
|
||||
public int callbackOrder => 10;
|
||||
|
||||
// Preprocess warnings don't show up in console
|
||||
bool warnSettingsNotSupported;
|
||||
|
||||
public void OnPreprocessBuild(BuildTarget target, string path) {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (HotReloadBuildHelper.IncludeInThisBuild()) {
|
||||
// move scriptable object into Resources/ folder
|
||||
HotReloadSettingsEditor.AddOrRemoveFromBuild(true);
|
||||
} else {
|
||||
// make sure HotReload resources are not in the build
|
||||
HotReloadSettingsEditor.AddOrRemoveFromBuild(false);
|
||||
|
||||
var options = HotReloadSettingsEditor.LoadSettingsOrDefault();
|
||||
var so = new SerializedObject(options);
|
||||
if (IncludeInBuildOption.I.GetValue(so)) {
|
||||
warnSettingsNotSupported = true;
|
||||
}
|
||||
}
|
||||
} catch (BuildFailedException) {
|
||||
throw;
|
||||
} catch (Exception ex) {
|
||||
throw new BuildFailedException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPostprocessBuild(BuildTarget target, string path) {
|
||||
if (warnSettingsNotSupported) {
|
||||
Debug.LogWarning(Translations.Errors.WarningBuildSettingsNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
// Do nothing in post build. settings asset will be dirty if build fails, so not worth fixing just for successful builds.
|
||||
// [PostProcessBuild]
|
||||
// private static void PostBuild(BuildTarget target, string pathToBuiltProject) {
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a63dd2d10359e94a8a3c24bf59164fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 254358
|
||||
packageName: Hot Reload | Edit Code Without Compiling
|
||||
packageVersion: 1.13.17
|
||||
assetPath: Packages/com.singularitygroup.hotreload/Editor/PlayerBuild/PrebuildIncludeResources.cs
|
||||
uploadId: 870414
|
||||
Reference in New Issue
Block a user