[Add] Hot Reload

This commit is contained in:
2026-02-27 03:16:18 +07:00
parent 5067cb51a1
commit b37579153b
431 changed files with 43054 additions and 1 deletions
@@ -0,0 +1,49 @@
using SingularityGroup.HotReload.Editor.Localization;
using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
internal class AllowAndroidAppToMakeHttpRequestsOption : ProjectOptionBase {
public override string ShortSummary {
get {
return Translations.Settings.OptionAllowHttpRequests;
}
}
public override string Summary => ShortSummary;
public override bool GetValue(SerializedObject so) {
#if UNITY_2022_1_OR_NEWER
// use PlayerSettings as the source of truth
return PlayerSettings.insecureHttpOption != InsecureHttpOption.NotAllowed;
#else
return GetProperty(so).boolValue;
#endif
}
public override string ObjectPropertyName =>
nameof(HotReloadSettingsObject.AllowAndroidAppToMakeHttpRequests);
public override void SetValue(SerializedObject so, bool value) {
base.SetValue(so, value);
// Enabling on Unity 2022 or newer → set the Unity option to Development Builds only
#if UNITY_2022_1_OR_NEWER
var notAllowed = PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed;
if (value) {
// user chose to enable it
if (notAllowed) {
PlayerSettings.insecureHttpOption = InsecureHttpOption.DevelopmentOnly;
}
} else {
// user chose to disable it
PlayerSettings.insecureHttpOption = InsecureHttpOption.NotAllowed;
}
#endif
}
public override void InnerOnGUI(SerializedObject so) {
var description = Translations.Settings.OptionAllowHttpRequestsDescription;
EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0a7442cee510ab4498ca2a846e0c4e92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 254358
packageName: Hot Reload | Edit Code Without Compiling
packageVersion: 1.13.17
assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/AllowAndroidAppToMakeHttpRequestsOption.cs
uploadId: 870414
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb8474c37f13d704d96b43e0f681680d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
/// <summary>
/// An option stored inside the current Unity project.
/// </summary>
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; }
/// <remarks>
/// Override this if your option is not needed for on-device Hot Reload to work.<br/>
/// (by default, a project option must be true for Hot Reload to work)
/// </remarks>
public virtual bool IsRequiredForBuild() {
return true;
}
}
/// <summary>
/// An option that is stored on the user's computer (shared between Unity projects).
/// </summary>
internal abstract class ComputerOptionBase : IOption {
public abstract string ShortSummary { get; }
public abstract string Summary { get; }
public abstract bool GetValue();
/// Uses <see cref="HotReloadPrefs"/> 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() { }
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: dab8ef53c2ee30a40ab6a7e4abd1260c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 254358
packageName: Hot Reload | Edit Code Without Compiling
packageVersion: 1.13.17
assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/HotReloadOptionBase.cs
uploadId: 870414
@@ -0,0 +1,34 @@
using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
public interface IOption {
string ShortSummary { get; }
string Summary { get; }
/// <param name="so">The <see cref="HotReloadSettingsObject"/> wrapped by SerializedObject</param>
bool GetValue(SerializedObject so);
/// <summary>
/// Handle the new value.
/// </summary>
/// <remarks>
/// Note: caller must skip calling this if value same as GetValue!
/// </remarks>
/// <param name="so">The <see cref="HotReloadSettingsObject"/> wrapped by SerializedObject</param>
/// <param name="value"></param>
void SetValue(SerializedObject so, bool value);
/// <param name="so">The <see cref="HotReloadSettingsObject"/> wrapped by SerializedObject</param>
void InnerOnGUI(SerializedObject so);
}
/// <summary>
/// An option scoped to the current Unity project.
/// </summary>
/// <remarks>
/// These options are intended to be shared with collaborators and used by Unity Player builds.
/// </remarks>
public interface ISerializedProjectOption {
string ObjectPropertyName { get; }
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0a626aa97160471f85de4646a634bdf1
timeCreated: 1674574633
AssetOrigin:
serializedVersion: 1
productId: 254358
packageName: Hot Reload | Edit Code Without Compiling
packageVersion: 1.13.17
assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/Base/OptionInterfaces.cs
uploadId: 870414
@@ -0,0 +1,70 @@
using System;
using System.Threading.Tasks;
using SingularityGroup.HotReload.Editor.Cli;
using SingularityGroup.HotReload.Editor.Localization;
using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
internal sealed class ExposeServerOption : ComputerOptionBase {
public override string ShortSummary => Translations.Settings.OptionExposeServerShort;
public override string Summary => Translations.Settings.OptionExposeServerFull;
public override void InnerOnGUI() {
string description;
if (GetValue()) {
description = Translations.Settings.OptionExposeServerDescriptionEnabled;
} else {
description = Translations.Settings.OptionExposeServerDescriptionDisabled;
}
EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle);
}
public override bool GetValue() {
return HotReloadPrefs.ExposeServerToLocalNetwork;
}
public override void SetValue(SerializedObject so, bool val) {
// AllowAndroidAppToMakeHttpRequestsOption
if (val == HotReloadPrefs.ExposeServerToLocalNetwork) {
return;
}
HotReloadPrefs.ExposeServerToLocalNetwork = val;
if (val) {
// they allowed this one for mobile builds, so now we allow everything else needed for player build to work with HR
new AllowAndroidAppToMakeHttpRequestsOption().SetValue(so, true);
}
RunTask(() => {
RunOnMainThreadSync(() => {
var isRunningResult = ServerHealthCheck.I.IsServerHealthy;
if (isRunningResult) {
var restartServer = EditorUtility.DisplayDialog(Translations.Dialogs.DialogTitleHotReload,
string.Format(Translations.Dialogs.DialogMessageRestartExposeServer, Summary),
Translations.Dialogs.DialogButtonRestartServer, Translations.Dialogs.DialogButtonDontRestart);
if (restartServer) {
CodePatcher.I.ClearPatchedMethods();
EditorCodePatcher.RestartCodePatcher().Forget();
}
}
});
});
}
void RunTask(Action action) {
var token = HotReloadWindow.Current.cancelToken;
Task.Run(() => {
if (token.IsCancellationRequested) return;
try {
action();
} catch (Exception ex) {
ThreadUtility.LogException(ex, token);
}
}, token);
}
void RunOnMainThreadSync(Action action) {
ThreadUtility.RunOnMainThread(action, HotReloadWindow.Current.cancelToken);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5ab0973d3ae1275469237480381842c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 254358
packageName: Hot Reload | Edit Code Without Compiling
packageVersion: 1.13.17
assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/ExposeServerOption.cs
uploadId: 870414
@@ -0,0 +1,25 @@
using SingularityGroup.HotReload.Editor.Localization;
using UnityEditor;
namespace SingularityGroup.HotReload.Editor {
internal class IncludeInBuildOption : ProjectOptionBase, ISerializedProjectOption {
static IncludeInBuildOption _I;
public static IncludeInBuildOption I = _I ?? (_I = new IncludeInBuildOption());
public override string ShortSummary => Translations.Settings.OptionIncludeInBuild;
public override string Summary => ShortSummary;
public override string ObjectPropertyName =>
nameof(HotReloadSettingsObject.IncludeInBuild);
public override void InnerOnGUI(SerializedObject so) {
string description;
if (GetValue(so)) {
description = Translations.Settings.OptionIncludeInBuildDescriptionEnabled;
} else {
description = Translations.Settings.OptionIncludeInBuildDescriptionDisabled;
}
description += Translations.Settings.OptionIncludeInBuildDescriptionSuffix;
EditorGUILayout.LabelField(description, HotReloadWindowStyles.WrapStyle);
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 39ed4f822bcd81340bdf7189b3bc5016
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 254358
packageName: Hot Reload | Edit Code Without Compiling
packageVersion: 1.13.17
assetPath: Packages/com.singularitygroup.hotreload/Editor/Window/GUI/Options/IncludeInBuildOption.cs
uploadId: 870414