// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once #include "../BinaryAsset.h" #include "Engine/Scripting/BinaryModule.h" #include "Engine/Visject/VisjectGraph.h" #define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250 #define VISUAL_SCRIPT_DEBUGGING USE_EDITOR #define VisualScriptGraphNode VisjectGraphNode<> class VisualScripting; class VisualScriptingBinaryModule; /// /// The Visual Script graph data. /// class VisualScriptGraph : public VisjectGraph { public: bool onNodeLoaded(Node* n) override; }; /// /// The Visual Script graph executor runtime. /// class VisualScriptExecutor : public VisjectExecutor { friend VisualScripting; public: /// /// Initializes a new instance of the class. /// VisualScriptExecutor(); private: Value eatBox(Node* caller, Box* box) override; Graph* GetCurrentGraph() const override; void ProcessGroupConstants(Box* box, Node* node, Value& value); void ProcessGroupPacking(Box* box, Node* node, Value& value); void ProcessGroupParameters(Box* box, Node* node, Value& value); void ProcessGroupTools(Box* box, Node* node, Value& value); void ProcessGroupFunction(Box* boxBase, Node* node, Value& value); void ProcessGroupFlow(Box* boxBase, Node* node, Value& value); }; /// /// The Visual Script asset. Contains a graph with functions and parameters for visual scripting. /// /// API_CLASS(NoSpawn, Sealed) class FLAXENGINE_API VisualScript : public BinaryAsset { DECLARE_BINARY_ASSET_HEADER(VisualScript, 1); friend VisualScripting; friend VisualScriptExecutor; friend VisualScriptingBinaryModule; public: /// /// Visual Script flag types. /// API_ENUM(Attributes="Flags") enum class Flags { /// /// No flags. /// None = 0, /// /// Script is abstract and cannot be instantiated directly. /// Abstract = 1, /// /// Script is sealed and cannot be inherited by other scripts. /// Sealed = 2, }; /// /// Visual Script metadata container. /// API_STRUCT() struct Metadata { DECLARE_SCRIPTING_TYPE_MINIMAL(Metadata); /// /// The base class typename. /// API_FIELD() String BaseTypename; /// /// The script flags. /// API_FIELD() Flags Flags; }; enum class MethodFlags { None = 0, Static = 1, Virtual = 2, Override = 4, }; struct Method { VisualScript* Script; VisualScriptGraphNode* Node; StringAnsi Name; MethodFlags MethodFlags; ScriptingTypeMethodSignature Signature; Array> ParamNames; }; struct Field { VisualScript* Script; VisjectGraphParameter* Parameter; int32 Index; StringAnsi Name; }; struct EventBinding { ScriptingTypeHandle Type; String Name; Array> BindedMethods; }; struct Instance { Array Params; Array EventBindings; }; private: Dictionary _instances; ScriptingTypeHandle _scriptingTypeHandle; ScriptingTypeHandle _scriptingTypeHandleCached; StringAnsiView _typename; char _typenameChars[33]; Array> _methods; Array> _fields; #if USE_EDITOR Array _oldParamsLayout; #endif public: /// /// The Visual Script graph. /// VisualScriptGraph Graph; /// /// The script metadata. /// API_FIELD(ReadOnly) Metadata Meta; public: /// /// Gets the typename of the Visual Script. Identifies it's scripting type. /// API_PROPERTY() FORCE_INLINE const StringAnsiView& GetScriptTypeName() const { return _typename; } /// /// Gets the list of Visual Script parameters declared in this graph (excluding base types). /// API_PROPERTY() const Array& GetParameters() const { return Graph.Parameters; } /// /// Gets the scripting type handle of this Visual Script. /// ScriptingTypeHandle GetScriptingType(); /// /// Creates a new instance of the Visual Script object. /// /// The created instance or null if failed. API_FUNCTION() ScriptingObject* CreateInstance(); /// /// Gets the Visual Script instance data. /// /// The object instance. /// The data or invalid instance (not VS or missing). Instance* GetScriptInstance(ScriptingObject* instance) const; /// /// Gets the value of the Visual Script parameter of the given instance. /// /// The parameter name. /// The object instance. /// The property value. API_FUNCTION() Variant GetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance) const; /// /// Sets the value of the Visual Script parameter of the given instance. /// /// The parameter name. /// The object instance. /// The property value to set. API_FUNCTION() void SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, const Variant& value) const; /// /// Sets the value of the Visual Script parameter of the given instance. /// /// The parameter name. /// The object instance. /// The property value to set. void SetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance, Variant&& value) const; /// /// Tries to find the method matching the given properties. Doesn't check base classes but just this script. /// /// The method name. /// The method parameters count. /// The method graph node entry for callback, null if not found. const Method* FindMethod(const StringAnsiView& name, int32 numParams = 0) const; /// /// Tries to find the field matching the given name. Doesn't check base classes but just this script. /// /// The field name. /// The field entry for access, null if not found. const Field* FindField(const StringAnsiView& name) const; /// /// Tries to load surface graph from the asset. /// /// The surface data or empty if failed to load it. API_FUNCTION() BytesContainer LoadSurface(); #if USE_EDITOR /// /// Updates the graph surface (save new one, discard cached data, reload asset). /// /// Stream with graph data. /// Script metadata. /// True if cannot save it, otherwise false. API_FUNCTION() bool SaveSurface(BytesContainer& data, API_PARAM(Ref) const Metadata& meta); // Returns the amount of methods in the script. API_FUNCTION() int32 GetMethodsCount() const { return _methods.Count(); } // Gets the signature data of the method. API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array& paramNames, API_PARAM(Out) Array& paramTypeNames, API_PARAM(Out) Array& paramOuts); // Gets the metadata of the script surface. API_FUNCTION() Span GetMetaData(int32 typeID); // Gets the metadata of the method. API_FUNCTION() Span GetMethodMetaData(int32 index, int32 typeID); #endif public: // [BinaryAsset] #if USE_EDITOR void GetReferences(Array& output) const override { // Base BinaryAsset::GetReferences(output); Graph.GetReferences(output); } #endif protected: // [BinaryAsset] LoadResult load() override; void unload(bool isReloading) override; AssetChunksFlag getChunksToPreload() const override; private: void CacheScriptingType(); }; /// /// The visual scripts module for engine scripting integration. /// /// class FLAXENGINE_API VisualScriptingBinaryModule : public BinaryModule { friend VisualScript; private: StringAnsi _name; public: /// /// Initializes a new instance of the class. /// VisualScriptingBinaryModule(); public: /// /// The visual script assets loaded into the module with exposes scripting types. Order matches the Types array. /// Array> Scripts; private: static ScriptingObject* VisualScriptObjectSpawn(const ScriptingObjectSpawnParams& params); #if USE_EDITOR void OnScriptsReloading(); #endif static void OnEvent(ScriptingObject* object, Span parameters, ScriptingTypeHandle eventType, StringView eventName); public: // [BinaryModule] const StringAnsi& GetName() const override; bool IsLoaded() const override; bool FindScriptingType(const StringAnsiView& typeName, int32& typeIndex) override; void* FindMethod(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, int32 numParams = 0) override; bool InvokeMethod(void* method, const Variant& instance, Span paramValues, Variant& result) override; void GetMethodSignature(void* method, ScriptingTypeMethodSignature& methodSignature) override; void* FindField(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name) override; void GetFieldSignature(void* field, ScriptingTypeFieldSignature& fieldSignature) override; bool GetFieldValue(void* field, const Variant& instance, Variant& result) override; bool SetFieldValue(void* field, const Variant& instance, Variant& value) override; void SerializeObject(JsonWriter& stream, ScriptingObject* object, const ScriptingObject* otherObj) override; void DeserializeObject(ISerializable::DeserializeStream& stream, ScriptingObject* object, ISerializeModifier* modifier) override; void OnObjectIdChanged(ScriptingObject* object, const Guid& oldId) override; void OnObjectDeleted(ScriptingObject* object) override; void Destroy(bool isReloading) override; }; /// /// The visual scripting runtime service. Allows to execute visual script functions with recursion support and thread-safety. /// class FLAXENGINE_API VisualScripting { public: struct NodeBoxValue { uint32 NodeId; uint32 BoxId; Variant Value; }; struct ScopeContext { // Method input parameters Span Parameters; // Invoke methods returned values cached within the scope (includes output parameters values) Array> ReturnedValues; // Function result to return Variant FunctionReturn; }; struct StackFrame { VisualScript* Script; VisualScriptGraphNode* Node; VisualScriptGraphNode::Box* Box; ScriptingObject* Instance; StackFrame* PreviousFrame; ScopeContext* Scope; }; /// /// Gets the top frame of the current thread Visual Script execution stack frame. /// static StackFrame* GetThreadStackTop(); /// /// Gets the current stack trace of the current thread Visual Script execution. /// /// The stack trace string. static String GetStackTrace(); /// /// Gets the binary module for the Visual Scripting. /// static VisualScriptingBinaryModule* GetBinaryModule(); /// /// Invokes the specified Visual Script method. /// /// The method to call. /// The object instance. Null for static methods. /// The input parameters array. Unused if method is parameter-less. /// The returned value. Undefined if method is void. static Variant Invoke(VisualScript::Method* method, ScriptingObject* instance, Span parameters = Span()); #if VISUAL_SCRIPT_DEBUGGING // Custom event that is called every time the Visual Script signal flows over the graph (including the data connections). Can be used to read nad visualize the Visual Script execution logic. static Action DebugFlow; /// /// Tries to evaluate a given script box value for the debugger (called usually during the breakpoint on the hanging thread). /// /// The script. /// The current object instance. /// The ID of the node in the script graph to evaluate it's box. /// The ID of the box in the node to evaluate it's value. /// The output value. Valid only if method returned true. /// True if could fetch teh value, otherwise false. static bool Evaluate(VisualScript* script, ScriptingObject* instance, uint32 nodeId, uint32 boxId, Variant& result); #endif };