#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEngine.InputSystem.Utilities; namespace UnityEngine.InputSystem.Editor { internal static class ProjectWideActionsAsset { private const string kDefaultAssetName = "InputSystem_Actions"; private const string kDefaultAssetPath = "Assets/" + kDefaultAssetName + ".inputactions"; private const string kDefaultTemplateAssetPath = "Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.json"; internal static class ProjectSettingsProjectWideActionsAssetConverter { class ProjectSettingsPostprocessor : AssetPostprocessor { #if UNITY_2021_2_OR_NEWER private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) #else private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) #endif { if (!Application.isPlaying) { // If the Library folder is deleted, InputSystem will fail to retrieve the assigned Project-wide Asset because this look-up occurs // during initialization while the Library is being rebuilt. So, afterwards perform another check and assign PWA asset if needed. var pwaAsset = ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild; if (InputSystem.actions == null && pwaAsset != null) InputSystem.actions = pwaAsset; } } } } // Returns the default asset path for where to create project-wide actions asset. internal static string defaultAssetPath => kDefaultAssetPath; // Returns the default template JSON content. internal static string GetDefaultAssetJson() { return File.ReadAllText(EditorHelpers.GetPhysicalPath(kDefaultTemplateAssetPath)); } // Creates an asset at the given path containing the default template JSON. internal static InputActionAsset CreateDefaultAssetAtPath(string assetPath = kDefaultAssetPath) { return CreateAssetAtPathFromJson(assetPath, File.ReadAllText(EditorHelpers.GetPhysicalPath(kDefaultTemplateAssetPath))); } // These may be moved out to internal types if decided to extend validation at a later point. /// /// Interface for reporting asset verification errors. /// internal interface IReportInputActionAssetVerificationErrors { /// /// Reports a failure to comply to requirements with a message meaningful to the user. /// /// User-friendly error message. void Report(string message); } /// /// Interface for asset verification. /// internal interface IInputActionAssetVerifier { /// /// Verifies the given asset. /// /// The asset to be verified /// The reporter to be used to report failure to meet requirements. public void Verify(InputActionAsset asset, IReportInputActionAssetVerificationErrors reporter); } /// /// Verifier managing verification and reporting of asset compliance with external requirements. /// class Verifier : IReportInputActionAssetVerificationErrors { private readonly IReportInputActionAssetVerificationErrors m_Reporter; // Default verification error reporter which generates feedback as debug warnings. private class DefaultInputActionAssetVerificationReporter : IReportInputActionAssetVerificationErrors { public void Report(string message) { Debug.LogWarning(message); } } /// /// Constructs a an instance associated with the given reporter. /// /// The associated reporter instance. If null, a default reporter will be constructed. public Verifier(IReportInputActionAssetVerificationErrors reporter = null) { m_Reporter = reporter ?? new DefaultInputActionAssetVerificationReporter(); errors = 0; } #region IReportInputActionAssetVerificationErrors interface /// public void Report(string message) { ++errors; try { m_Reporter.Report(message); } catch (Exception e) { // Only log unexpected but non-fatal exception Debug.LogException(e); } } #endregion /// /// Returns the total number of errors seen in verification (accumulative). /// public int errors { get; private set; } /// /// Returns true if the number of reported errors in verification is zero, else false. /// public bool isValid => errors == 0; private static List> s_VerifierFactories; /// /// Registers a factory instance. /// /// The factory instance. /// true if successfully added, false if the factory have already been registered. public static bool RegisterFactory(Func factory) { if (s_VerifierFactories == null) s_VerifierFactories = new List>(1); if (s_VerifierFactories.Contains(factory)) return false; s_VerifierFactories.Add(factory); return true; } /// /// Unregisters a factory instance that has previously been registered. /// /// The factory instance to be removed. /// true if successfully unregistered, false if the given factory instance could not be found. public static bool UnregisterFactory(Func factory) { return s_VerifierFactories.Remove(factory); } /// /// Verifies the given project-wide input action asset using all registered verifiers. /// /// The asset to be verified. /// true if no verification errors occurred, else false. /// /// Throws System.ArgumentNullException if asset is null. /// /// If any registered factory and/or verifier instance throws an exception this will be evaluated /// as a verification error since the execution of the verifier could not continue. However, any /// exceptions thrown will be caught and logged but not stop execution of the calling thread. /// bool Verify(InputActionAsset asset) { if (asset == null) throw new ArgumentNullException(nameof(asset)); if (s_VerifierFactories == null || s_VerifierFactories.Count == 0) return true; var instance = new Verifier(m_Reporter); foreach (var factory in s_VerifierFactories) { try { factory.Invoke().Verify(asset, instance); } catch (Exception e) { // Only log unexpected but non-fatal exception and count to fail verification ++errors; Debug.LogException(e); } } return errors == 0; } /// /// Verifies the given project-wide input action asset using all registered verifiers. /// /// The asset to be verified. /// The reporter to be used. If this argument is null the default reporter will be used. /// true if no verification errors occurred, else false. /// Throws System.ArgumentNullException if asset is null. public static bool Verify(InputActionAsset asset, IReportInputActionAssetVerificationErrors reporter = null) { return (s_VerifierFactories == null || s_VerifierFactories.Count == 0) || new Verifier(reporter).Verify(asset); } } internal static bool Verify(InputActionAsset asset, IReportInputActionAssetVerificationErrors reporter = null) { return Verifier.Verify(asset, reporter); } internal static bool RegisterInputActionAssetVerifier(Func factory) { return Verifier.RegisterFactory(factory); } internal static bool UnregisterInputActionAssetVerifier(Func factory) { return Verifier.UnregisterFactory(factory); } // Creates an asset at the given path containing the given JSON content. private static InputActionAsset CreateAssetAtPathFromJson(string assetPath, string json) { // Note that the extra work here is to override the JSON name from the source asset var inputActionAsset = InputActionAsset.FromJson(json); inputActionAsset.name = InputActionImporter.NameFromAssetPath(assetPath); InputActionAssetManager.SaveAsset(assetPath, inputActionAsset.ToJson()); return AssetDatabase.LoadAssetAtPath(assetPath); } } } #endif // UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS