BITFALL/Assets/Plugins/FastScriptReload/Scripts/Editor/FastScriptReloadWelcomeScre...

874 lines
45 KiB
C#
Raw Normal View History

2023-06-08 14:09:50 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using FastScriptReload.Editor.Compilation;
using FastScriptReload.Editor.Compilation.ScriptGenerationOverrides;
using FastScriptReload.Runtime;
using ImmersiveVRTools.Editor.Common.Utilities;
using ImmersiveVRTools.Editor.Common.WelcomeScreen;
using ImmersiveVRTools.Editor.Common.WelcomeScreen.GuiElements;
using ImmersiveVRTools.Editor.Common.WelcomeScreen.PreferenceDefinition;
using ImmersiveVRTools.Editor.Common.WelcomeScreen.Utilities;
using ImmersiveVrToolsCommon.Runtime.Logging;
using UnityEditor;
using UnityEditor.Compilation;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Rendering;
namespace FastScriptReload.Editor
{
public class FastScriptReloadWelcomeScreen : ProductWelcomeScreenBase
{
public static string BaseUrl = "https://immersivevrtools.com";
public static string GenerateGetUpdatesUrl(string userId, string versionId)
{
//WARN: the URL can sometimes be adjusted, make sure updated correctly
return $"{BaseUrl}/updates/fast-script-reload/{userId}?CurrentVersion={versionId}";
}
public static string VersionId = "1.4";
private static readonly string ProjectIconName = "ProductIcon64";
public static readonly string ProjectName = "fast-script-reload";
private static Vector2 _WindowSizePx = new Vector2(650, 500);
private static string _WindowTitle = "Fast Script Reload";
public static ChangeMainViewButton ExclusionsSection { get; private set; }
public static ChangeMainViewButton EditorHotReloadSection { get; private set; }
public static ChangeMainViewButton NewFieldsSection { get; private set; }
public static ChangeMainViewButton UserScriptRewriteOverrides { get; private set; }
public void OpenExclusionsSection()
{
ExclusionsSection.OnClick(this);
}
public void OpenUserScriptRewriteOverridesSection()
{
UserScriptRewriteOverrides.OnClick(this);
}
public void OpenEditorHotReloadSection()
{
EditorHotReloadSection.OnClick(this);
}
public void OpenNewFieldsSection()
{
NewFieldsSection.OnClick(this);
}
private static readonly ScrollViewGuiSection MainScrollViewSection = new ScrollViewGuiSection(
"", (screen) =>
{
GenerateCommonWelcomeText(FastScriptReloadPreference.ProductName, screen);
GUILayout.Label("Enabled Features:", screen.LabelStyle);
using (LayoutHelper.LabelWidth(350))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableAutoReloadForChangedFiles);
RenderSettingsWithCheckLimitationsButton(FastScriptReloadPreference.EnableExperimentalAddedFieldsSupport, true, () => ((FastScriptReloadWelcomeScreen)screen).OpenNewFieldsSection());
RenderSettingsWithCheckLimitationsButton(FastScriptReloadPreference.EnableExperimentalEditorHotReloadSupport, false, () => ((FastScriptReloadWelcomeScreen)screen).OpenEditorHotReloadSection());
}
}
);
private static void RenderSettingsWithCheckLimitationsButton(ToggleProjectEditorPreferenceDefinition preferenceDefinition, bool allowChange, Action onCheckLimitationsClick)
{
EditorGUILayout.BeginHorizontal();
if (!allowChange)
{
using (LayoutHelper.LabelWidth(313))
{
EditorGUILayout.LabelField(preferenceDefinition.Label);
}
}
else
{
ProductPreferenceBase.RenderGuiAndPersistInput(preferenceDefinition);
}
if (GUILayout.Button("Check limitations"))
{
onCheckLimitationsClick();
}
EditorGUILayout.EndHorizontal();
}
private static readonly List<GuiSection> LeftSections = CreateLeftSections(new List<ChangeMainViewButton>
{
new ChangeMainViewButton("On-Device\r\nHot-Reload",
(screen) =>
{
EditorGUILayout.LabelField("Live Script Reload", screen.BoldTextStyle);
GUILayout.Space(10);
EditorGUILayout.LabelField(@"There's an extension to this asset that'll allow you to include Hot-Reload capability in builds (standalone / Android), please click the button below to learn more.", screen.TextStyle);
GUILayout.Space(20);
if (GUILayout.Button("View Live Script Reload on Asset Store"))
{
Application.OpenURL($"{RedirectBaseUrl}/live-script-reload-extension");
}
}
)
},
new LaunchSceneButton("Basic Example", (s) => GetScenePath("ExampleScene"), (screen) =>
{
GUILayout.Label(
$@"Asset is very simple to use:
1) Hit play to start.
2) Go to 'FunctionLibrary.cs' ({@"Assets/FastScriptReload/Examples/Scripts/"})", screen.TextStyle);
CreateOpenFunctionLibraryOnRippleMethodButton();
GUILayout.Label(
$@"3) Change 'Ripple' method (eg change line before return statement to 'p.z = v * 10'
4) Save file
5) See change immediately",
screen.TextStyle
);
GUILayout.Space(10);
EditorGUILayout.HelpBox("There are some limitations to what can be Hot-Reloaded, documentation lists them under 'limitations' section.", MessageType.Warning);
}), MainScrollViewSection);
protected static List<GuiSection> CreateLeftSections(List<ChangeMainViewButton> additionalSections, LaunchSceneButton launchSceneButton, ScrollViewGuiSection mainScrollViewSection)
{
return new List<GuiSection>() {
new GuiSection("", new List<ClickableElement>
{
new LastUpdateButton("New Update!", (screen) => LastUpdateUpdateScrollViewSection.RenderMainScrollViewSection(screen)),
new ChangeMainViewButton("Welcome", (screen) => mainScrollViewSection.RenderMainScrollViewSection(screen)),
}),
new GuiSection("Options", new List<ClickableElement>
{
new ChangeMainViewButton("Reload", (screen) =>
{
const int sectionBreakHeight = 15;
GUILayout.Label(
@"Asset watches all script files and automatically hot-reloads on change, you can disable that behaviour and reload on demand.",
screen.TextStyle
);
using (LayoutHelper.LabelWidth(320))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableAutoReloadForChangedFiles);
}
GUILayout.Space(sectionBreakHeight);
EditorGUILayout.HelpBox("On demand reload :\r\n(only works if you opted in below, this is to avoid unnecessary file watching)\r\nvia Window -> Fast Script Reload -> Force Reload, \r\nor by calling 'FastScriptIterationManager.Instance.TriggerReloadForChangedFiles()'", MessageType.Warning);
using (LayoutHelper.LabelWidth(320))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableOnDemandReload);
}
GUILayout.Space(sectionBreakHeight);
GUILayout.Label(
@"For performance reasons script changes are batched are reloaded every N seconds",
screen.TextStyle
);
using (LayoutHelper.LabelWidth(300))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.BatchScriptChangesAndReloadEveryNSeconds);
}
GUILayout.Space(sectionBreakHeight);
using (LayoutHelper.LabelWidth(350))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableExperimentalThisCallLimitationFix);
}
EditorGUILayout.HelpBox("Method calls utilizing 'this' will trigger compiler exception, if enabled tool will rewrite the calls to have proper type after adjustments." +
"\r\n\r\nIn case you're seeing compile errors relating to 'this' keyword please let me know via support page. Also turning this setting off will prevent rewrite.", MessageType.Info);
GUILayout.Space(sectionBreakHeight);
using (LayoutHelper.LabelWidth(350))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.IsForceLockAssembliesViaCode);
}
EditorGUILayout.HelpBox(
@"Sometimes Unity continues to reload assemblies on change in playmode even when Auto-Refresh is turned off.
Use this setting to force lock assemblies via code."
, MessageType.Info);
GUILayout.Space(sectionBreakHeight);
using (LayoutHelper.LabelWidth(350))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.IsDidFieldsOrPropertyCountChangedCheckDisabled);
}
EditorGUILayout.HelpBox("By default if you add / remove fields, tool will not redirect method calls for recompiled class." +
"\r\nYou can also enable added-fields support (experimental)." +
"\r\n\r\nSome assets however will use IL weaving to adjust your classes (eg Mirror) as a post compile step. In that case it's quite likely hot-reload will still work. " +
"\r\n\r\nTick this box for tool to try and reload changes when that happens."
, MessageType.Info);
}),
(UserScriptRewriteOverrides = new ChangeMainViewButton("User Script\r\nRewrite Overrides", (screen) =>
{
EditorGUILayout.HelpBox(
$@"For tool to work it'll need to slightly adjust your code to make it compilable. Sometimes due to existing limitations this can fail and you'll see an error.
You can specify custom script rewrite overrides, those are specified for specific parts of code that fail, eg method.
It will help overcome limitations in the short run while I work on implementing proper solution."
, MessageType.Info);
EditorGUILayout.HelpBox(
$@"To add:
1) right-click in project panel on the file that causes the issue.
2) select Fast Script Reload -> Add / Open User Script Rewrite Override
It'll open override file with template already in. You can read top comments that describe how to use it."
, MessageType.Warning);
EditorGUILayout.LabelField("Existing User Defined Script Overrides:", screen.BoldTextStyle);
Action executeAfterIteration = null;
foreach (var scriptOverride in ScriptGenerationOverridesManager.UserDefinedScriptOverrides)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(scriptOverride.File.Name);
if (GUILayout.Button("Open"))
{
InternalEditorUtility.OpenFileAtLineExternal(scriptOverride.File.FullName, 0);
}
if (GUILayout.Button("Delete"))
{
executeAfterIteration = () =>
{
if (EditorUtility.DisplayDialog("Are you sure", "This will permanently remove override file.", "Delete", "Keep File"))
{
ScriptGenerationOverridesManager.TryRemoveScriptOverride(scriptOverride);
}
};
}
EditorGUILayout.EndHorizontal();
}
executeAfterIteration?.Invoke();
})),
(ExclusionsSection = new ChangeMainViewButton("Exclusions", (screen) =>
{
EditorGUILayout.HelpBox("Those are easiest to manage from Project window by right clicking on script file and selecting: " +
"\r\nFast Script Reload -> Add Hot-Reload Exclusion " +
"\r\nFast Script Reload -> Remove Hot-Reload Exclusion", MessageType.Info);
GUILayout.Space(10);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.FilesExcludedFromHotReload);
})),
new ChangeMainViewButton("Debugging", (screen) =>
{
EditorGUILayout.HelpBox(
@"To debug you'll need to set breakpoints in dynamically-compiled file.
BREAKPOINTS IN ORIGINAL FILE WON'T BE HIT!", MessageType.Error);
EditorGUILayout.HelpBox(
@"You can do that via:
- clicking link in console-window after change, eg
'FSR: Files: FunctionLibrary.cs changed (click here to debug [in bottom details pane]) (...)'
(it needs to be clicked in bottom details pane, double click will simply take you to log location)", MessageType.Warning);
GUILayout.Space(10);
EditorGUILayout.HelpBox(@"Tool can also auto-open generated file on every change, to do so select below option", MessageType.Info);
using (LayoutHelper.LabelWidth(350))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.IsAutoOpenGeneratedSourceFileOnChangeEnabled);
}
GUILayout.Space(20);
using (LayoutHelper.LabelWidth(350))
{
EditorGUILayout.LabelField("Logging", screen.BoldTextStyle);
GUILayout.Space(5);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableDetailedDebugLogging);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.LogHowToFixMessageOnCompilationError);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.StopShowingAutoReloadEnabledDialogBox);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.DebugWriteRewriteReasonAsComment);
}
})
}.Concat(additionalSections).ToList()),
new GuiSection("Experimental", new List<ClickableElement>
{
(NewFieldsSection = new ChangeMainViewButton("New Fields", (screen) =>
{
#if LiveScriptReload_Enabled
EditorGUILayout.HelpBox(
@"On Device Reload (in running build) - Not Supported
If you enable - new fields WILL show in editor and work as expected but link with the device will be broken and changes won't be visible there!", MessageType.Error);
GUILayout.Space(10);
#endif
EditorGUILayout.HelpBox(
@"Adding new fields is still in experimental mode, it will have issues.
When you encounter them please get in touch (via any support links above) and I'll be sure to sort them out. Thanks!", MessageType.Error);
GUILayout.Space(10);
EditorGUILayout.HelpBox(
@"Adding new fields will affect performance, behind the scenes your code is rewritten to access field via static dictionary.
Once you exit playmode and do a full recompile they'll turn to standard fields as you'd expect.
New fields will also show in editor - you can tweak them as normal variables.", MessageType.Warning);
GUILayout.Space(10);
EditorGUILayout.HelpBox(
@"LIMITATIONS: (full list and more info in docs)
- outside classes can not call new fields added at runtime
- new fields will only show in editor if they were already used at least once", MessageType.Info);
GUILayout.Space(10);
using (LayoutHelper.LabelWidth(300))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableExperimentalAddedFieldsSupport);
}
GUILayout.Space(10);
if (Application.isPlaying)
{
EditorGUILayout.HelpBox(@"You're in playmode, for option to start working you need to restart playmode.", MessageType.Warning);
}
GUILayout.Space(10);
})),
(EditorHotReloadSection = new ChangeMainViewButton("Editor Hot-Reload", (screen) =>
{
EditorGUILayout.HelpBox(@"Currently asset hot-reloads only in play-mode, you can enable experimental editor mode support here.
Please make sure to read limitation section as not all changes can be performed", MessageType.Warning);
EditorGUILayout.HelpBox(@"As an experimental feature it may be unstable and is not as reliable as play-mode workflow.
In some cases it can lock/crash editor.", MessageType.Error);
GUILayout.Space(10);
using (LayoutHelper.LabelWidth(320))
{
var valueBefore = (bool)FastScriptReloadPreference.EnableExperimentalEditorHotReloadSupport.GetEditorPersistedValueOrDefault();
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.EnableExperimentalEditorHotReloadSupport);
var valueAfter = (bool)FastScriptReloadPreference.EnableExperimentalEditorHotReloadSupport.GetEditorPersistedValueOrDefault();
if (!valueBefore && valueAfter)
{
EditorUtility.DisplayDialog("Experimental feature",
"Reloading outside of playmode is still in experimental phase. " +
"\r\n\r\nIt's not as good as in-playmode workflow",
"Ok");
#if UNITY_2019_3_OR_NEWER
CompilationPipeline.RequestScriptCompilation();
#elif UNITY_2017_1_OR_NEWER
var editorAssembly = Assembly.GetAssembly(typeof(Editor));
var editorCompilationInterfaceType = editorAssembly.GetType("UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface");
var dirtyAllScriptsMethod = editorCompilationInterfaceType.GetMethod("DirtyAllScripts", BindingFlags.Static | BindingFlags.Public);
dirtyAllScriptsMethod.Invoke(editorCompilationInterfaceType, null);
#endif
}
}
GUILayout.Space(10);
EditorGUILayout.HelpBox(@"Tool will automatically trigger full domain reload after number of hot-reloads specified below has been reached.
This is to ensure dynamically created and loaded assembles are cleared out properly", MessageType.Info);
GUILayout.Space(10);
using (LayoutHelper.LabelWidth(420))
{
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.TriggerDomainReloadIfOverNDynamicallyLoadedAssembles);
}
GUILayout.Space(10);
}))
}),
new GuiSection("Advanced", new List<ClickableElement>
{
new ChangeMainViewButton("File Watchers", (screen) =>
{
EditorGUILayout.HelpBox(
$@"Asset watches .cs files for changes. Unfortunately Unity's FileWatcher
implementation has some performance issues.
By default all project directories can be watched, you can adjust that here.
path - which directory to watch
filter - narrow down files to match filter, eg all *.cs files (*.cs)
includeSubdirectories - whether child directories should be watched as well
{FastScriptReloadManager.FileWatcherReplacementTokenForApplicationDataPath} - you can use that token and it'll be replaced with your /Assets folder"
, MessageType.Info);
EditorGUILayout.HelpBox("Recompile after making changes for file watchers to re-load.", MessageType.Warning);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.FileWatcherSetupEntries);
}),
new ChangeMainViewButton("Exclude References", (screen) =>
{
EditorGUILayout.HelpBox(
$@"Asset pulls in all the references from changed assembly. If you're encountering some compilation errors relating to those - please use list below to exclude specific ones."
, MessageType.Info);
EditorGUILayout.HelpBox($@"By default asset removes ExCSS.Unity as it collides with the Tuple type. If you need that library in changed code - please remove from the list", MessageType.Warning);
ProductPreferenceBase.RenderGuiAndPersistInput(FastScriptReloadPreference.ReferencesExcludedFromHotReload);
})
}),
new GuiSection("Launch Demo", new List<ClickableElement>
{
launchSceneButton
})
};
}
private static readonly string RedirectBaseUrl = "https://immersivevrtools.com/redirect/fast-script-reload";
private static readonly GuiSection TopSection = CreateTopSectionButtons(RedirectBaseUrl);
protected static GuiSection CreateTopSectionButtons(string redirectBaseUrl)
{
return new GuiSection("Support", new List<ClickableElement>
{
new OpenUrlButton("Documentation", $"{redirectBaseUrl}/documentation"),
new OpenUrlButton("Discord", $"{redirectBaseUrl}/discord"),
new OpenUrlButton("Unity Forum", $"{redirectBaseUrl}/unity-forum"),
new OpenUrlButton("Contact", $"{redirectBaseUrl}/contact")
}
);
}
private static readonly GuiSection BottomSection = new GuiSection(
"I want to make this tool better. And I need your help!",
$"It'd be great if you could share your feedback (good and bad) with me. I'm very keen to make this tool better and that can only happen with your help. Please use:",
new List<ClickableElement>
{
new OpenUrlButton(" Unity Forum", $"{RedirectBaseUrl}/unity-forum"),
new OpenUrlButton(" or Write a Short Review", $"{RedirectBaseUrl}/asset-store-review"),
}
);
public override string WindowTitle { get; } = _WindowTitle;
public override Vector2 WindowSizePx { get; } = _WindowSizePx;
#if !LiveScriptReload_Enabled
[MenuItem("Window/Fast Script Reload/Start Screen", false, 1999)]
#endif
public static FastScriptReloadWelcomeScreen Init()
{
return OpenWindow<FastScriptReloadWelcomeScreen>(_WindowTitle, _WindowSizePx);
}
#if !LiveScriptReload_Enabled
[MenuItem("Window/Fast Script Reload/Force Reload", true, 1999)]
#endif
public static bool ForceReloadValidate()
{
return EditorApplication.isPlaying && (bool)FastScriptReloadPreference.EnableOnDemandReload.GetEditorPersistedValueOrDefault();
}
#if !LiveScriptReload_Enabled
[MenuItem("Window/Fast Script Reload/Force Reload", false, 1999)]
#endif
public static void ForceReload()
{
if (!(bool)FastScriptReloadPreference.EnableOnDemandReload.GetEditorPersistedValueOrDefault())
{
LoggerScoped.LogWarning("On demand hot reload is disabled, can't perform. You can enable it via 'Window -> Fast Script Reload -> Start Screen -> Reload -> Enable on demand reload'");
return;
}
FastScriptReloadManager.Instance.TriggerReloadForChangedFiles();
}
public void OnEnable()
{
OnEnableCommon(ProjectIconName);
}
public void OnGUI()
{
RenderGUI(LeftSections, TopSection, BottomSection, MainScrollViewSection);
}
protected static void CreateOpenFunctionLibraryOnRippleMethodButton()
{
if (GUILayout.Button("Open 'FunctionLibrary.cs'"))
{
var codeComponent = AssetDatabase.LoadAssetAtPath<MonoScript>(AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets($"t:Script FunctionLibrary")[0]));
CodeEditorManager.GotoScript(codeComponent, "Ripple");
}
}
}
public class FastScriptReloadPreference : ProductPreferenceBase
{
public const string BuildSymbol_DetailedDebugLogging = "ImmersiveVrTools_DebugEnabled";
public const string ProductName = "Fast Script Reload";
private static string[] ProductKeywords = new[] { "productivity", "tools" };
public static readonly IntProjectEditorPreferenceDefinition BatchScriptChangesAndReloadEveryNSeconds = new IntProjectEditorPreferenceDefinition(
"Batch script changes and reload every N seconds", "BatchScriptChangesAndReloadEveryNSeconds", 1);
public static readonly ToggleProjectEditorPreferenceDefinition EnableAutoReloadForChangedFiles = new ToggleProjectEditorPreferenceDefinition(
"Enable auto Hot-Reload for changed files (in play mode)", "EnableAutoReloadForChangedFiles", true);
public static readonly ToggleProjectEditorPreferenceDefinition EnableOnDemandReload = new ToggleProjectEditorPreferenceDefinition(
"Enable on demand hot reload", "EnableOnDemandReload", false);
public static readonly ToggleProjectEditorPreferenceDefinition EnableExperimentalThisCallLimitationFix = new ToggleProjectEditorPreferenceDefinition(
"(Experimental) Enable method calls with 'this' as argument fix", "EnableExperimentalThisCallLimitationFix", true, (object newValue, object oldValue) =>
{
DynamicCompilationBase.EnableExperimentalThisCallLimitationFix = (bool)newValue;
},
(value) =>
{
DynamicCompilationBase.EnableExperimentalThisCallLimitationFix = (bool)value;
});
public static readonly StringListProjectEditorPreferenceDefinition FilesExcludedFromHotReload = new StringListProjectEditorPreferenceDefinition(
"Files excluded from Hot-Reload", "FilesExcludedFromHotReload", new List<string> {}, isReadonly: true);
public static readonly StringListProjectEditorPreferenceDefinition ReferencesExcludedFromHotReload = new StringListProjectEditorPreferenceDefinition(
"References to exclude from Hot-Reload", "ReferencesExcludedFromHotReload", new List<string>
{
"ExCSS.Unity.dll"
}, (newValue, oldValue) =>
{
DynamicCompilationBase.ReferencesExcludedFromHotReload = (List<string>)newValue;
},
(value) =>
{
DynamicCompilationBase.ReferencesExcludedFromHotReload = (List<string>)value;
});
public static readonly ToggleProjectEditorPreferenceDefinition LogHowToFixMessageOnCompilationError = new ToggleProjectEditorPreferenceDefinition(
"Log how to fix message on compilation error", "LogHowToFixMessageOnCompilationError", true, (object newValue, object oldValue) =>
{
DynamicCompilationBase.LogHowToFixMessageOnCompilationError = (bool)newValue;
},
(value) =>
{
DynamicCompilationBase.LogHowToFixMessageOnCompilationError = (bool)value;
}
);
public static readonly ToggleProjectEditorPreferenceDefinition DebugWriteRewriteReasonAsComment = new ToggleProjectEditorPreferenceDefinition(
"Write rewrite reason as comment in changed file", "DebugWriteRewriteReasonAsComment", false, (object newValue, object oldValue) =>
{
DynamicCompilationBase.DebugWriteRewriteReasonAsComment = (bool)newValue;
},
(value) =>
{
DynamicCompilationBase.DebugWriteRewriteReasonAsComment = (bool)value;
});
public static readonly ToggleProjectEditorPreferenceDefinition IsAutoOpenGeneratedSourceFileOnChangeEnabled = new ToggleProjectEditorPreferenceDefinition(
"Auto-open generated source file for debugging", "IsAutoOpenGeneratedSourceFileOnChangeEnabled", false);
public static readonly ToggleProjectEditorPreferenceDefinition StopShowingAutoReloadEnabledDialogBox = new ToggleProjectEditorPreferenceDefinition(
"Stop showing assets/script auto-reload enabled warning", "StopShowingAutoReloadEnabledDialogBox", false);
public static readonly ToggleProjectEditorPreferenceDefinition EnableDetailedDebugLogging = new ToggleProjectEditorPreferenceDefinition(
"Enable detailed debug logging", "EnableDetailedDebugLogging", false,
(object newValue, object oldValue) =>
{
BuildDefineSymbolManager.SetBuildDefineSymbolState(BuildSymbol_DetailedDebugLogging, (bool)newValue);
},
(value) =>
{
BuildDefineSymbolManager.SetBuildDefineSymbolState(BuildSymbol_DetailedDebugLogging, (bool)value);
}
);
public static readonly ToggleProjectEditorPreferenceDefinition IsDidFieldsOrPropertyCountChangedCheckDisabled = new ToggleProjectEditorPreferenceDefinition(
"Disable added/removed fields check", "IsDidFieldsOrPropertyCountChangedCheckDisabled", false,
(object newValue, object oldValue) =>
{
FastScriptReloadManager.Instance.AssemblyChangesLoaderEditorOptionsNeededInBuild.IsDidFieldsOrPropertyCountChangedCheckDisabled = (bool)newValue;
},
(value) =>
{
FastScriptReloadManager.Instance.AssemblyChangesLoaderEditorOptionsNeededInBuild.IsDidFieldsOrPropertyCountChangedCheckDisabled = (bool)value;
}
);
public static readonly ToggleProjectEditorPreferenceDefinition IsForceLockAssembliesViaCode = new ToggleProjectEditorPreferenceDefinition(
"Force prevent assembly reload during playmode", "IsForceLockAssembliesViaCode", false);
public static readonly JsonObjectListProjectEditorPreferenceDefinition<FileWatcherSetupEntry> FileWatcherSetupEntries = new JsonObjectListProjectEditorPreferenceDefinition<FileWatcherSetupEntry>(
"File Watchers Setup", "FileWatcherSetupEntries", new List<string>
{
JsonUtility.ToJson(new FileWatcherSetupEntry("<Application.dataPath>", "*.cs", true))
},
() => new FileWatcherSetupEntry("<Application.dataPath>", "*.cs", true)
);
public static readonly ToggleProjectEditorPreferenceDefinition EnableExperimentalAddedFieldsSupport = new ToggleProjectEditorPreferenceDefinition(
"(Experimental) Enable runtime added field support", "EnableExperimentalAddedFieldsSupport", true,
(object newValue, object oldValue) =>
{
FastScriptReloadManager.Instance.AssemblyChangesLoaderEditorOptionsNeededInBuild.EnableExperimentalAddedFieldsSupport = (bool)newValue;
},
(value) =>
{
FastScriptReloadManager.Instance.AssemblyChangesLoaderEditorOptionsNeededInBuild.EnableExperimentalAddedFieldsSupport = (bool)value;
});
public static readonly ToggleProjectEditorPreferenceDefinition EnableExperimentalEditorHotReloadSupport = new ToggleProjectEditorPreferenceDefinition(
"(Experimental) Enable Hot-Reload outside of play mode", "EnableExperimentalEditorHotReloadSupport", false);
//TODO: potentially that's just a normal settings (also in playmode) - but in playmode user is unlikely to make this many changes
public static readonly IntProjectEditorPreferenceDefinition TriggerDomainReloadIfOverNDynamicallyLoadedAssembles = new IntProjectEditorPreferenceDefinition(
"Trigger full domain reload after N hot-reloads (when not in play mode)", "TriggerDomainReloadIfOverNDynamicallyLoadedAssembles", 50);
public static void SetCommonMaterialsShader(ShadersMode newShaderModeValue)
{
var rootToolFolder = AssetPathResolver.GetAssetFolderPathRelativeToScript(ScriptableObject.CreateInstance(typeof(FastScriptReloadWelcomeScreen)), 1);
if (rootToolFolder.Contains("/Scripts"))
{
rootToolFolder = rootToolFolder.Replace("/Scripts", ""); //if nested remove that and go higher level
}
var assets = AssetDatabase.FindAssets("t:Material Point", new[] { rootToolFolder });
try
{
Shader shaderToUse = null;
switch (newShaderModeValue)
{
case ShadersMode.HDRP: shaderToUse = Shader.Find("Shader Graphs/Point URP"); break;
case ShadersMode.URP: shaderToUse = Shader.Find("Shader Graphs/Point URP"); break;
case ShadersMode.Surface: shaderToUse = Shader.Find("Graph/Point Surface"); break;
default:
throw new ArgumentOutOfRangeException();
}
foreach (var guid in assets)
{
var material = AssetDatabase.LoadAssetAtPath<Material>(AssetDatabase.GUIDToAssetPath(guid));
if (material.shader.name != shaderToUse.name)
{
material.shader = shaderToUse;
}
}
}
catch (Exception ex)
{
LoggerScoped.LogWarning($"Shader does not exist: {ex.Message}");
}
}
public static List<ProjectEditorPreferenceDefinitionBase> PreferenceDefinitions = new List<ProjectEditorPreferenceDefinitionBase>()
{
CreateDefaultShowOptionPreferenceDefinition(),
BatchScriptChangesAndReloadEveryNSeconds,
EnableAutoReloadForChangedFiles,
EnableExperimentalThisCallLimitationFix,
LogHowToFixMessageOnCompilationError,
StopShowingAutoReloadEnabledDialogBox,
IsDidFieldsOrPropertyCountChangedCheckDisabled,
FileWatcherSetupEntries,
IsAutoOpenGeneratedSourceFileOnChangeEnabled,
EnableExperimentalAddedFieldsSupport,
ReferencesExcludedFromHotReload,
EnableExperimentalEditorHotReloadSupport,
TriggerDomainReloadIfOverNDynamicallyLoadedAssembles,
IsForceLockAssembliesViaCode
};
private static bool PrefsLoaded = false;
#if !LiveScriptReload_Enabled
#if UNITY_2019_1_OR_NEWER
[SettingsProvider]
public static SettingsProvider ImpostorsSettings()
{
return GenerateProvider(ProductName, ProductKeywords, PreferencesGUI);
}
#else
[PreferenceItem(ProductName)]
#endif
#endif
public static void PreferencesGUI()
{
if (!PrefsLoaded)
{
LoadDefaults(PreferenceDefinitions);
PrefsLoaded = true;
}
RenderGuiCommon(PreferenceDefinitions);
}
public enum ShadersMode
{
HDRP,
URP,
Surface
}
}
#if !LiveScriptReload_Enabled
[InitializeOnLoad]
#endif
public class FastScriptReloadWelcomeScreenInitializer : WelcomeScreenInitializerBase
{
#if !LiveScriptReload_Enabled
static FastScriptReloadWelcomeScreenInitializer()
{
var userId = ProductPreferenceBase.CreateDefaultUserIdDefinition(FastScriptReloadWelcomeScreen.ProjectName).GetEditorPersistedValueOrDefault().ToString();
HandleUnityStartup(
() => FastScriptReloadWelcomeScreen.Init(),
FastScriptReloadWelcomeScreen.GenerateGetUpdatesUrl(userId, FastScriptReloadWelcomeScreen.VersionId),
new List<ProjectEditorPreferenceDefinitionBase>(),
(isFirstRun) =>
{
AutoDetectAndSetShaderMode();
}
);
InitCommon();
}
#endif
protected static void InitCommon()
{
DisplayMessageIfLastDetourPotentiallyCrashedEditor();
EnsureUserAwareOfAutoRefresh();
DynamicCompilationBase.LogHowToFixMessageOnCompilationError = (bool)FastScriptReloadPreference.LogHowToFixMessageOnCompilationError.GetEditorPersistedValueOrDefault();
DynamicCompilationBase.DebugWriteRewriteReasonAsComment = (bool)FastScriptReloadPreference.DebugWriteRewriteReasonAsComment.GetEditorPersistedValueOrDefault();
DynamicCompilationBase.ReferencesExcludedFromHotReload = (List<string>)FastScriptReloadPreference.ReferencesExcludedFromHotReload.GetElements();
FastScriptReloadManager.Instance.AssemblyChangesLoaderEditorOptionsNeededInBuild.UpdateValues(
(bool)FastScriptReloadPreference.IsDidFieldsOrPropertyCountChangedCheckDisabled.GetEditorPersistedValueOrDefault(),
(bool)FastScriptReloadPreference.EnableExperimentalAddedFieldsSupport.GetEditorPersistedValueOrDefault()
);
BuildDefineSymbolManager.SetBuildDefineSymbolState(FastScriptReloadPreference.BuildSymbol_DetailedDebugLogging,
(bool)FastScriptReloadPreference.EnableDetailedDebugLogging.GetEditorPersistedValueOrDefault()
);
}
private static void EnsureUserAwareOfAutoRefresh()
{
var autoRefreshMode = (AssetPipelineAutoRefreshMode)EditorPrefs.GetInt("kAutoRefreshMode", EditorPrefs.GetBool("kAutoRefresh") ? 1 : 0);
if (autoRefreshMode != AssetPipelineAutoRefreshMode.Enabled)
return;
if ((bool)FastScriptReloadPreference.IsForceLockAssembliesViaCode.GetEditorPersistedValueOrDefault())
return;
LoggerScoped.LogWarning("Fast Script Reload - asset auto refresh enabled - full reload will be triggered unless editor preference adjusted - see documentation for more details.");
if ((bool)FastScriptReloadPreference.StopShowingAutoReloadEnabledDialogBox.GetEditorPersistedValueOrDefault())
return;
var chosenOption = EditorUtility.DisplayDialogComplex("Fast Script Reload - Warning",
"Auto reload for assets/scripts is enabled." +
$"\n\nThis means any change made in playmode will likely trigger full recompile." +
$"\r\n\r\nIt's an editor setting and can be adjusted at any time via Edit -> Preferences -> Asset Pipeline -> Auto Refresh" +
$"\r\n\r\nI can also adjust that for you now - that means you'll need to manually load changes (outside of playmode) via Assets -> Refresh (CTRL + R)." +
$"\r\n\r\nIn some editor versions you can also set script compilation to happen outside of playmode and don't have to manually refresh. " +
$"\r\n\r\nDepending on version you'll find it via: " +
$"\r\n1) Edit -> Preferences -> General -> Script Changes While Playing -> Recompile After Finished Playing." +
$"\r\n2) Edit -> Preferences -> Asset Pipeline -> Auto Refresh -> Enabled Outside Playmode",
"Ok, disable asset auto refresh (I'll refresh manually when needed)",
"No, don't change (stop showing this message)",
"No, don't change"
);
switch (chosenOption)
{
// change.
case 0:
EditorPrefs.SetInt("kAutoRefreshMode", (int)AssetPipelineAutoRefreshMode.Disabled);
EditorPrefs.SetInt("kAutoRefresh", 0); //older unity versions
break;
// don't change and stop showing message.
case 1:
FastScriptReloadPreference.StopShowingAutoReloadEnabledDialogBox.SetEditorPersistedValue(true);
break;
// don't change
case 2:
break;
default:
LoggerScoped.LogError("Unrecognized option.");
break;
}
}
//copied from internal UnityEditor.AssetPipelineAutoRefreshMode
internal enum AssetPipelineAutoRefreshMode
{
Disabled,
Enabled,
EnabledOutsidePlaymode,
}
private static void DisplayMessageIfLastDetourPotentiallyCrashedEditor()
{
const string firstInitSessionKey = "FastScriptReloadWelcomeScreenInitializer_FirstInitDone";
if (!SessionState.GetBool(firstInitSessionKey, false))
{
SessionState.SetBool(firstInitSessionKey, true);
var lastDetour = DetourCrashHandler.RetrieveLastDetour();
if (!string.IsNullOrEmpty(lastDetour))
{
EditorUtility.DisplayDialog("Fast Script Reload",
$@"That's embarrassing!
It seems like I've crashed your editor, sorry!
Last detoured method was: '{lastDetour}'
If this happens again, please reach out via support and we'll sort it out.
In the meantime, you can exclude any file from Hot-Reload by
1) right-clicking on .cs file in Project menu
2) Fast Script Reload
3) Add Hot-Reload Exclusion
", "Ok");
DetourCrashHandler.ClearDetourLog();
}
}
}
protected static void AutoDetectAndSetShaderMode()
{
var usedShaderMode = FastScriptReloadPreference.ShadersMode.Surface;
var renderPipelineAsset = GraphicsSettings.renderPipelineAsset;
if (renderPipelineAsset == null)
{
usedShaderMode = FastScriptReloadPreference.ShadersMode.Surface;
}
else if (renderPipelineAsset.GetType().Name.Contains("HDRenderPipelineAsset"))
{
usedShaderMode = FastScriptReloadPreference.ShadersMode.HDRP;
}
else if (renderPipelineAsset.GetType().Name.Contains("UniversalRenderPipelineAsset"))
{
usedShaderMode = FastScriptReloadPreference.ShadersMode.URP;
}
FastScriptReloadPreference.SetCommonMaterialsShader(usedShaderMode);
}
}
}