From de55ad90b75c23e5ed7c14d96335a3daa829c286 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 20:57:53 -0600 Subject: [PATCH 1/5] Add require script attribute and functionality. --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 70 ++++++++++++++++++- .../Editor/RequireScriptAttribute.cs | 25 +++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..9da2ab180 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -224,16 +224,44 @@ namespace FlaxEditor.CustomEditors.Dedicated private void AddScripts(List items) { - var actions = new List(4); + var actions = new List(); for (int i = 0; i < items.Count; i++) { var scriptType = items[i]; + List requiredScripts = new List(); + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + requiredScripts.Add(new ScriptType(attribute.RequiredType)); + } + } + } var actors = ScriptsEditor.ParentEditor.Values; for (int j = 0; j < actors.Count; j++) { var actor = (Actor)actors[j]; actions.Add(AddRemoveScript.Add(actor, scriptType)); + // Check if actor has required scripts and add them if the actor does not. + foreach (var type in requiredScripts) + { + if (actor.GetScript(type.Type) != null) + continue; + actions.Add(AddRemoveScript.Add(actor, type)); + } } } @@ -584,10 +612,50 @@ namespace FlaxEditor.CustomEditors.Dedicated var values = new ScriptsContainer(elementType, i, Values); var scriptType = TypeUtils.GetObjectType(script); var editor = CustomEditorsUtil.CreateEditor(scriptType, false); + + // Check if actor has all the required scripts + bool hasAllRequiredScripts = true; + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var scriptTypesToCheck = new List(); + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + scriptTypesToCheck.Add(new ScriptType(attribute.RequiredType)); + } + } + + if (scriptTypesToCheck.Count > 0) + { + foreach (var type in scriptTypesToCheck) + { + var requiredScript = script.Actor.GetScript(type.Type); + if (requiredScript == null) + { + Editor.LogWarning($"{script} on {script.Actor} is missing required script of type {type}."); + hasAllRequiredScripts = false; + break; + } + } + } + } // Create group var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name); var group = layout.Group(title, editor); + if (!hasAllRequiredScripts) + group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed; if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { if (Editor.Instance.ProjectCache.IsCollapsedGroup(title)) diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs new file mode 100644 index 000000000..eee529c5c --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace FlaxEngine; + +/// +/// This attribute is used to check for if a script requires another script type. +/// +[Serializable] +[AttributeUsage(AttributeTargets.Class)] +public class RequireScriptAttribute : Attribute +{ + /// + /// The required type. + /// + public Type RequiredType; + + /// + /// Initializes a new instance of the class. + /// + /// The required type. + public RequireScriptAttribute(Type type) + { + RequiredType = type; + } +} From c4c3a3a5e8f0743cbaca227deda933583d88c9f3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:01:19 -0600 Subject: [PATCH 2/5] Remove break to show all missing required scripts in logs. --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 9da2ab180..91a89bba7 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -643,9 +643,8 @@ namespace FlaxEditor.CustomEditors.Dedicated var requiredScript = script.Actor.GetScript(type.Type); if (requiredScript == null) { - Editor.LogWarning($"{script} on {script.Actor} is missing required script of type {type}."); + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required script of type {type}."); hasAllRequiredScripts = false; - break; } } } From 44e55cc8b6a3c5fb3cce5f1de828d9489fed7bf3 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:25:40 -0600 Subject: [PATCH 3/5] Add require Actor attribute --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 62 ++++++++++++++++--- .../Editor/RequireActorAttribute.cs | 25 ++++++++ 2 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 91a89bba7..93a8ec6be 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -242,18 +242,43 @@ namespace FlaxEditor.CustomEditors.Dedicated if (attributes.Count > 0) { - foreach (var attribute in attributes) + foreach (var e in attributes) { - if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + if (!e.RequiredType.IsSubclassOf(typeof(Script))) continue; - requiredScripts.Add(new ScriptType(attribute.RequiredType)); + requiredScripts.Add(new ScriptType(e.RequiredType)); } } } + + // See if script requires a specific actor type + RequireActorAttribute actorAttribute = null; + if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) + { + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireActorAttribute requireActorAttribute) + continue; + actorAttribute = requireActorAttribute; + break; + } + } + var actors = ScriptsEditor.ParentEditor.Values; for (int j = 0; j < actors.Count; j++) { var actor = (Actor)actors[j]; + + // If required actor exists but is not this actor type then skip adding to actor + if (actorAttribute != null) + { + if (actor.GetType() != actorAttribute.RequiredType && !actor.GetType().IsSubclassOf(actorAttribute.RequiredType)) + { + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} not added to {actor} due to script requiring an Actor type of {actorAttribute.RequiredType}."); + continue; + } + } + actions.Add(AddRemoveScript.Add(actor, scriptType)); // Check if actor has required scripts and add them if the actor does not. foreach (var type in requiredScripts) @@ -614,7 +639,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var editor = CustomEditorsUtil.CreateEditor(scriptType, false); // Check if actor has all the required scripts - bool hasAllRequiredScripts = true; + bool hasAllRequirements = true; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { var scriptTypesToCheck = new List(); @@ -643,17 +668,40 @@ namespace FlaxEditor.CustomEditors.Dedicated var requiredScript = script.Actor.GetScript(type.Type); if (requiredScript == null) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required script of type {type}."); - hasAllRequiredScripts = false; + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Script of type {type}."); + hasAllRequirements = false; } } } } + if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) + { + var scriptTypesToCheck = new List(); + RequireActorAttribute attribute = null; + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireActorAttribute requireActorAttribute) + continue; + attribute = requireActorAttribute; + break; + } + + if (attribute != null) + { + var actor = script.Actor; + if (actor.GetType() != attribute.RequiredType && !actor.GetType().IsSubclassOf(attribute.RequiredType)) + { + Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Actor of type {attribute.RequiredType}."); + hasAllRequirements = false; + // Maybe call to remove script here? + } + } + } // Create group var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name); var group = layout.Group(title, editor); - if (!hasAllRequiredScripts) + if (!hasAllRequirements) group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed; if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs new file mode 100644 index 000000000..6e08217bf --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/RequireActorAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace FlaxEngine; + +/// +/// This attribute is used to check for if a script requires an Actor type. +/// +[Serializable] +[AttributeUsage(AttributeTargets.Class)] +public class RequireActorAttribute : Attribute +{ + /// + /// The required type. + /// + public Type RequiredType; + + /// + /// Initializes a new instance of the class. + /// + /// The required type. + public RequireActorAttribute(Type type) + { + RequiredType = type; + } +} From 2ac8480df4c311177efeaced06bcfcd3873c8eb5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:45:33 -0600 Subject: [PATCH 4/5] Simplify RequireScriptAttribute code. --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 60 ++++++++----------- .../Editor/RequireScriptAttribute.cs | 13 +++- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 93a8ec6be..6692eb5f0 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -229,25 +229,15 @@ namespace FlaxEditor.CustomEditors.Dedicated for (int i = 0; i < items.Count; i++) { var scriptType = items[i]; - List requiredScripts = new List(); + RequireScriptAttribute scriptAttribute = null; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { - var attributes = new List(); foreach (var e in scriptType.GetAttributes(false)) { if (e is not RequireScriptAttribute requireScriptAttribute) continue; - attributes.Add(requireScriptAttribute); - } - - if (attributes.Count > 0) - { - foreach (var e in attributes) - { - if (!e.RequiredType.IsSubclassOf(typeof(Script))) - continue; - requiredScripts.Add(new ScriptType(e.RequiredType)); - } + scriptAttribute = requireScriptAttribute; + break; } } @@ -274,18 +264,26 @@ namespace FlaxEditor.CustomEditors.Dedicated { if (actor.GetType() != actorAttribute.RequiredType && !actor.GetType().IsSubclassOf(actorAttribute.RequiredType)) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} not added to {actor} due to script requiring an Actor type of {actorAttribute.RequiredType}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` not added to `{actor}` due to script requiring an Actor type of `{actorAttribute.RequiredType}`."); continue; } } actions.Add(AddRemoveScript.Add(actor, scriptType)); // Check if actor has required scripts and add them if the actor does not. - foreach (var type in requiredScripts) + if (scriptAttribute != null) { - if (actor.GetScript(type.Type) != null) - continue; - actions.Add(AddRemoveScript.Add(actor, type)); + foreach (var type in scriptAttribute.RequiredTypes) + { + if (!type.IsSubclassOf(typeof(Script))) + { + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(type.Name)}` not added to `{actor}` due to the class not being a subclass of Script."); + continue; + } + if (actor.GetScript(type) != null) + continue; + actions.Add(AddRemoveScript.Add(actor, new ScriptType(type))); + } } } } @@ -642,33 +640,24 @@ namespace FlaxEditor.CustomEditors.Dedicated bool hasAllRequirements = true; if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) { - var scriptTypesToCheck = new List(); - var attributes = new List(); + RequireScriptAttribute scriptAttribute = null; foreach (var e in scriptType.GetAttributes(false)) { if (e is not RequireScriptAttribute requireScriptAttribute) continue; - attributes.Add(requireScriptAttribute); + scriptAttribute = requireScriptAttribute; } - if (attributes.Count > 0) + if (scriptAttribute != null) { - foreach (var attribute in attributes) + foreach (var type in scriptAttribute.RequiredTypes) { - if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + if (!type.IsSubclassOf(typeof(Script))) continue; - scriptTypesToCheck.Add(new ScriptType(attribute.RequiredType)); - } - } - - if (scriptTypesToCheck.Count > 0) - { - foreach (var type in scriptTypesToCheck) - { - var requiredScript = script.Actor.GetScript(type.Type); + var requiredScript = script.Actor.GetScript(type); if (requiredScript == null) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Script of type {type}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` on `{script.Actor}` is missing a required Script of type `{type}`."); hasAllRequirements = false; } } @@ -676,7 +665,6 @@ namespace FlaxEditor.CustomEditors.Dedicated } if (scriptType.HasAttribute(typeof(RequireActorAttribute), false)) { - var scriptTypesToCheck = new List(); RequireActorAttribute attribute = null; foreach (var e in scriptType.GetAttributes(false)) { @@ -691,7 +679,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var actor = script.Actor; if (actor.GetType() != attribute.RequiredType && !actor.GetType().IsSubclassOf(attribute.RequiredType)) { - Editor.LogWarning($"{Utilities.Utils.GetPropertyNameUI(scriptType.Name)} on {script.Actor} is missing a required Actor of type {attribute.RequiredType}."); + Editor.LogWarning($"`{Utilities.Utils.GetPropertyNameUI(scriptType.Name)}` on `{script.Actor}` is missing a required Actor of type `{attribute.RequiredType}`."); hasAllRequirements = false; // Maybe call to remove script here? } diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs index eee529c5c..9b3c4ccd1 100644 --- a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -12,7 +12,7 @@ public class RequireScriptAttribute : Attribute /// /// The required type. /// - public Type RequiredType; + public Type[] RequiredTypes; /// /// Initializes a new instance of the class. @@ -20,6 +20,15 @@ public class RequireScriptAttribute : Attribute /// The required type. public RequireScriptAttribute(Type type) { - RequiredType = type; + RequiredTypes = new[] { type }; + } + + /// + /// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class. + /// + /// The required types. + public RequireScriptAttribute(Type[] types) + { + RequiredTypes = types; } } From 9de408e4e886d4455dcff1c6f3ed25cb707d7dfd Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 16 Nov 2023 21:46:37 -0600 Subject: [PATCH 5/5] Fix comments --- .../Scripting/Attributes/Editor/RequireScriptAttribute.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs index 9b3c4ccd1..414a13aeb 100644 --- a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -3,19 +3,19 @@ using System; namespace FlaxEngine; /// -/// This attribute is used to check for if a script requires another script type. +/// This attribute is used to check for if a script requires other script types. /// [Serializable] [AttributeUsage(AttributeTargets.Class)] public class RequireScriptAttribute : Attribute { /// - /// The required type. + /// The required types. /// public Type[] RequiredTypes; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The required type. public RequireScriptAttribute(Type type) @@ -24,7 +24,7 @@ public class RequireScriptAttribute : Attribute } /// - /// Initializes a new instance of the <see cref="RequireScriptAttribute"/> class. + /// Initializes a new instance of the class. /// /// The required types. public RequireScriptAttribute(Type[] types)