A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR && ENABLE_INPUT_SYSTEM && UNITY_2023_2_OR_NEWER
2using System.Collections.Generic;
3using UnityEditor;
4using UnityEngine.InputSystem.Editor;
5
6namespace UnityEngine.InputSystem.Plugins.InputForUI
7{
8 // Unlike InputSystemProvider we want the verifier to register itself directly on domain reload in editor.
9 [InitializeOnLoad]
10 internal class InputActionAssetVerifier : ProjectWideActionsAsset.IInputActionAssetVerifier
11 {
12 public enum ReportPolicy
13 {
14 ReportAll,
15 SuppressChildErrors
16 }
17
18 // Note: This is intentionally not a constant to avoid dead code warning in tests while this remains
19 // as a setting type of value.
20 public static ReportPolicy DefaultReportPolicy = ReportPolicy.SuppressChildErrors;
21
22 static InputActionAssetVerifier()
23 {
24 // Register an InputActionAsset verifier for this plugin.
25 ProjectWideActionsAsset.RegisterInputActionAssetVerifier(() => new InputActionAssetVerifier());
26
27 InputSystemProvider.SetOnRegisterActions((asset) => { ProjectWideActionsAsset.Verify(asset); });
28 }
29
30 #region ProjectWideActionsAsset.IInputActionAssetVerifier
31
32 public void Verify(InputActionAsset asset,
33 ProjectWideActionsAsset.IReportInputActionAssetVerificationErrors reporter)
34 {
35 // Note that we never cache this to guarantee we have the current configuration.
36 var config = InputSystemProvider.Configuration.GetDefaultConfiguration();
37 Verify(asset, ref config, reporter);
38 }
39
40 #endregion
41
42 private struct Context
43 {
44 const string errorSuffix = "The Input System's runtime UI integration relies on certain required input action definitions, some of which are missing. This means some runtime UI input may not work correctly. See <a href=\"https://docs.unity3d.com/Packages/com.unity.inputsystem@latest/index.html?subfolder=/manual/UISupport.html#required-actions-for-ui\">Input System Manual - UI Support</a> for guidance on required actions for UI integration or see <a href=\"https://docs.unity3d.com/Packages/com.unity.inputsystem@latest/index.html?subfolder=/manual/ProjectWideActions.html#the-default-actions\">how to revert to defaults</a>.";
45
46 public Context(InputActionAsset asset,
47 ProjectWideActionsAsset.IReportInputActionAssetVerificationErrors reporter,
48 ReportPolicy policy)
49 {
50 this.asset = asset;
51 this.missingPaths = new HashSet<string>();
52 this.reporter = reporter;
53 this.policy = policy;
54 }
55
56 private string GetAssetReference()
57 {
58 var path = AssetDatabase.GetAssetPath(asset);
59 return path ?? asset.name;
60 }
61
62 private void ActionMapWarning(string actionMap, string problem)
63 {
64 reporter.Report($"InputActionMap with path '{actionMap}' in asset \"{GetAssetReference()}\" {problem}. {errorSuffix}");
65 }
66
67 private void ActionWarning(string actionNameOrId, string problem)
68 {
69 reporter.Report($"InputAction with path '{actionNameOrId}' in asset \"{GetAssetReference()}\" {problem}. {errorSuffix}");
70 }
71
72 public void Verify(string actionNameOrId, InputActionType actionType, string expectedControlType)
73 {
74 var action = asset.FindAction(actionNameOrId);
75 if (action == null)
76 {
77 const string kCouldNotBeFound = "could not be found";
78
79 // Check if the map (if any) exists
80 var noMapOrMapExists = true;
81 var index = actionNameOrId.IndexOf('/');
82 if (index > 0)
83 {
84 var path = actionNameOrId.Substring(0, index);
85 if (asset.FindActionMap(path) == null)
86 {
87 if (missingPaths == null)
88 missingPaths = new HashSet<string>(1);
89 if (missingPaths.Add(path))
90 ActionMapWarning(path, kCouldNotBeFound);
91 noMapOrMapExists = false;
92 }
93 }
94
95 if (!noMapOrMapExists && policy == ReportPolicy.SuppressChildErrors)
96 return;
97
98 ActionWarning(actionNameOrId, kCouldNotBeFound);
99 }
100 else if (action.bindings.Count == 0)
101 ActionWarning(actionNameOrId, "do not have any configured bindings");
102 else if (action.type != actionType)
103 ActionWarning(actionNameOrId, $"has 'type' set to '{nameof(InputActionType)}.{action.type}', but '{nameof(InputActionType)}.{actionType}' was expected");
104 else if (!string.IsNullOrEmpty(expectedControlType) && !string.IsNullOrEmpty(action.expectedControlType) && action.expectedControlType != expectedControlType)
105 ActionWarning(actionNameOrId, $"has 'expectedControlType' set to '{action.expectedControlType}', but '{expectedControlType}' was expected");
106 }
107
108 private readonly InputActionAsset asset;
109 private readonly ProjectWideActionsAsset.IReportInputActionAssetVerificationErrors reporter;
110
111 private HashSet<string> missingPaths; // Avoids generating multiple warnings around missing map
112 private ReportPolicy policy;
113 }
114
115 private static void Verify(InputActionAsset asset, ref InputSystemProvider.Configuration config,
116 ProjectWideActionsAsset.IReportInputActionAssetVerificationErrors reporter)
117 {
118 // Note:
119 // PWA has initial state check true for "Point" action, DefaultActions do not, does it matter?
120 //
121 // Additionally note that "Submit" and "Cancel" are indirectly expected to be of Button action type.
122 // This is not available in UI configuration, but InputActionRebindingExtensions suggests this.
123 //
124 // Additional "LeftClick" has initial state check set in PWA, but not "MiddleClick" and "RightClick".
125 // Is this intentional? Are requirements different?
126 var context = new Context(asset, reporter, DefaultReportPolicy);
127 context.Verify(actionNameOrId: config.PointAction, actionType: InputActionType.PassThrough, expectedControlType: nameof(Vector2));
128 context.Verify(actionNameOrId: config.MoveAction, actionType: InputActionType.PassThrough, expectedControlType: nameof(Vector2));
129 context.Verify(actionNameOrId: config.SubmitAction, actionType: InputActionType.Button, expectedControlType: "Button");
130 context.Verify(actionNameOrId: config.CancelAction, actionType: InputActionType.Button, expectedControlType: "Button");
131 context.Verify(actionNameOrId: config.LeftClickAction, actionType: InputActionType.PassThrough, expectedControlType: "Button");
132 context.Verify(actionNameOrId: config.MiddleClickAction, actionType: InputActionType.PassThrough, expectedControlType: "Button");
133 context.Verify(actionNameOrId: config.RightClickAction, actionType: InputActionType.PassThrough, expectedControlType: "Button");
134 context.Verify(actionNameOrId: config.ScrollWheelAction, actionType: InputActionType.PassThrough, expectedControlType: nameof(Vector2));
135 }
136 }
137}
138
139#endif // UNITY_EDITOR && ENABLE_INPUT_SYSTEM && UNITY_2023_2_OR_NEWER