using System.IO; using System.Linq; using UnityEditor; using UnityEngine; namespace Unity.VisualScripting { // Important, see static constructor below for more details [InitializeOnLoad] public static class VSUsageUtility { /* Need to be careful with this constructor: * A major difference between Visual Scripting and other packages is that VS is installed by default in all projects * This makes it discoverable, but also means that potentially all users pay the cost of our background processes * * This utility let's us isolate users who *actually* are using Visual Scripting from users who aren't. * For those who aren't, we want to minimize the cost they pay. InitializeOnLoad triggers on every domain reload * so we need to be careful with what we do in this constructor and cache what we can to avoid calculation. */ static VSUsageUtility() { CheckAndSetIsVisualScriptingUsed(); DoInitializeOnLoadCalls(); } private static void CheckAndSetIsVisualScriptingUsed() { // Using the SessionState API, this lets us cache our boolean persisted through domain reloads, but cleared // on Unity exit var sessionStateSet = SessionState.GetBool("Unity.VisualScripting.IsVisualScriptingUsedSet", false); if (sessionStateSet) { isVisualScriptingUsed = SessionState.GetBool("Unity.VisualScripting.IsVisualScriptingUsed", false); return; } // Everything under here is only calculated once per Unity session start (project open) var isVSUsed = false; // If we want to check on any state once per editor start-up that tells us if VS is being used, do that here // in batchmode or when someone builds a project using vs without opening a graph themselves, we still need // to consider it used to ensure the AOTPrebuilder/link.xml generation still kicks in isVSUsed = AssetDatabase.IsValidFolder(PluginPaths.ASSETS_FOLDER_BOLT_GENERATED); isVSUsed = isVSUsed || IsOlderVersionOfVSUsed(); isVisualScriptingUsed = isVSUsed; } // Note: This is currently only possible because all our InitializeOnLoad calls were in Core. If we ever // need to put something in Flow or State, we might need to move this entire class to a shared assembly that // references those assemblies. private static void DoInitializeOnLoadCalls() { if (!isVisualScriptingUsed) return; if (_initializeOnLoadExecuted) return; _initializeOnLoadExecuted = true; EditorDebugUtility.DeleteDebugLogFile(); EditorPlatformUtility.InitializeActiveBuildTarget(); EditorApplicationUtility.Initialize(); EditorTimeUtility.Initialize(); UnityAPI.Initialize(); PackageEventListener.SubscribeToEvents(); PluginContainer.InitializeOnLoad(); XmlDocumentation.Initialize(); } private static bool IsOlderVersionOfVSUsed() { // Instantiating our deprecated loaders explicitly to avoid plugin container / plugin initialization logic // Is ordered in descending order (newest versions first) var deprecatedLoaders = new PluginDeprecatedSavedVersionLoader[] { new DeprecatedSavedVersionLoader_1_6_1(null), new DeprecatedSavedVersionLoader_1_5_1(null), new DeprecatedSavedVersionLoader_Bolt_AssetStore(null) }; // We don't need to use the old loaders for every plugin, just core, because we just want to know *if* // an older version is being used or not return deprecatedLoaders.Select(savedVersionLoader => savedVersionLoader.Run(out var loadedVersion)) .Any(success => success); } public static bool isVisualScriptingUsed { get => _isVisualScriptingUsed; set { SessionState.SetBool("Unity.VisualScripting.IsVisualScriptingUsedSet", true); SessionState.SetBool("Unity.VisualScripting.IsVisualScriptingUsed", value); if (_isVisualScriptingUsed == value) return; _isVisualScriptingUsed = value; // The first time we determine that Visual Scripting is being used, we trigger other things if (_isVisualScriptingUsed) { DoInitializeOnLoadCalls(); // Ensure our savedVersion is serialized for future migrations BoltCore.Manifest.savedVersion = BoltCore.Manifest.savedVersion; // Ensure that our nodes / units are imported and cached PluginContainer.ImportUnits(); } } } private static bool _isVisualScriptingUsed = false; // Used to protect against doing the initialize on load calls twice in the same app domain frame. // This can only happen on editor start, if we set _isVisualScriptingUsed to true private static bool _initializeOnLoadExecuted = false; } }