Expose Raw BC Properties to Node Palette.

This commit is contained in:
chcurran
2021-05-05 14:59:08 -07:00
parent 181ce42591
commit a30d8eb886
30 changed files with 2856 additions and 69 deletions
@@ -88,6 +88,13 @@ namespace ScriptCanvas
Current,
};
enum class PropertyStatus : AZ::u8
{
Getter,
None,
Setter,
};
struct VersionData
{
AZ_TYPE_INFO(VersionData, "{14C629F6-467B-46FE-8B63-48FDFCA42175}");
@@ -33,6 +33,8 @@ namespace ScriptCanvas
Event,
Free,
Member,
Getter,
Setter,
Count,
};
@@ -394,6 +394,7 @@ namespace ScriptCanvas
AZ::Uuid m_type = AZ::Uuid::CreateNull();
AZStd::string m_className;
AZStd::string m_methodName;
PropertyStatus m_propertyStatus = PropertyStatus::None;
bool IsValid()
{
@@ -104,8 +104,7 @@ namespace ScriptCanvas
const FunctorOut& Nodeable::GetExecutionOutChecked(size_t index) const
{
if (index >= m_outs.size() && m_outs[index])
if (index >= m_outs.size() || !m_outs[index])
{
return m_noOpFunctor;
}
@@ -537,6 +537,20 @@ namespace ScriptCanvas
return azrtti_istypeof<const ScriptCanvas::Nodes::Logic::Break*>(execution->GetId().m_node);
}
bool IsClassPropertyRead(ExecutionTreeConstPtr execution)
{
return execution->GetSymbol() == Symbol::FunctionCall
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)
&& azrtti_cast<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Getter;
}
bool IsClassPropertyWrite(ExecutionTreeConstPtr execution)
{
return execution->GetSymbol() == Symbol::FunctionCall
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)
&& azrtti_cast<const ScriptCanvas::Nodes::Core::Method*>(execution->GetId().m_node)->GetPropertyStatus() == PropertyStatus::Setter;
}
bool IsCodeConstructable(Grammar::VariableConstPtr value)
{
return Data::IsValueType(value->m_datum.GetType())
@@ -1280,7 +1294,6 @@ namespace ScriptCanvas
return identifier;
}
ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointsResolved& nextEndpoints, AZStd::unordered_set<const Slot*>& previousIns, GraphExecutionPathTraversalListener& listener);
ExecutionTraversalResult TraverseExecutionConnectionsRecurse(const EndpointResolved& in, AZStd::unordered_set<const Slot*>& previousIns, GraphExecutionPathTraversalListener& listener);
@@ -79,6 +79,10 @@ namespace ScriptCanvas
bool IsBreak(const ExecutionTreeConstPtr& execution);
bool IsClassPropertyRead(ExecutionTreeConstPtr execution);
bool IsClassPropertyWrite(ExecutionTreeConstPtr execution);
bool IsCodeConstructable(VariableConstPtr value);
bool IsCycle(const Node& node);
@@ -193,6 +193,22 @@ namespace ScriptCanvas
return DynamicDataType::Any;
}
PropertyStatus Method::GetPropertyStatus() const
{
switch (m_methodType)
{
case MethodType::Getter:
return PropertyStatus::Getter;
case MethodType::Setter:
return PropertyStatus::Setter;
default:
return PropertyStatus::None;
}
}
void Method::InitializeMethod(const MethodConfiguration& config)
{
m_namespaces = config.m_namespaces ? *config.m_namespaces : m_namespaces;
@@ -239,7 +255,7 @@ namespace ScriptCanvas
OnInitializeOutputPost(outputConfig);
}
void Method::InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName)
void Method::InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
@@ -255,13 +271,13 @@ namespace ScriptCanvas
{
InitializeFree(namespaces, methodName);
}
else if (auto ebusIterator = behaviorContext->m_ebuses.find(className); ebusIterator == behaviorContext->m_ebuses.end())
else if (auto ebusIterator = behaviorContext->m_ebuses.find(className); ebusIterator != behaviorContext->m_ebuses.end())
{
InitializeClass(namespaces, className, methodName);
InitializeEvent(namespaces, className, methodName);
}
else
{
InitializeEvent(namespaces, className, methodName);
InitializeClass(namespaces, className, methodName, propertyStatus);
}
}
@@ -291,7 +307,7 @@ namespace ScriptCanvas
}
}
void Method::InitializeClass(const NamespacePath&, AZStd::string_view className, AZStd::string_view methodName)
void Method::InitializeClass(const NamespacePath&, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
@@ -299,9 +315,11 @@ namespace ScriptCanvas
const AZ::BehaviorClass* bcClass{};
AZStd::string prettyClassName;
if (BehaviorContextUtils::FindClass(method, bcClass, className, methodName, &prettyClassName))
if (BehaviorContextUtils::FindClass(method, bcClass, className, methodName, propertyStatus, &prettyClassName))
{
MethodConfiguration config(*method, MethodType::Member);
const auto methodType = propertyStatus == PropertyStatus::None ? MethodType::Member : propertyStatus == PropertyStatus::Getter ? MethodType::Getter : MethodType::Setter;
MethodConfiguration config(*method, methodType);
config.m_class = bcClass;
config.m_namespaces = &m_namespaces;
config.m_className = &className;
@@ -647,8 +665,12 @@ namespace ScriptCanvas
break;
case MethodType::Member:
case MethodType::Getter:
case MethodType::Setter:
{
if (BehaviorContextUtils::FindClass(method, bcClass, m_className, methodName, nullptr, m_warnOnMissingFunction))
PropertyStatus status = m_methodType == MethodType::Getter ? PropertyStatus::Getter : m_methodType == MethodType::Setter ? PropertyStatus::Setter : PropertyStatus::None;
if (BehaviorContextUtils::FindClass(method, bcClass, m_className, methodName, status, nullptr, m_warnOnMissingFunction))
{
outClass = bcClass;
outMethod = method;
@@ -87,14 +87,13 @@ namespace ScriptCanvas
bool IsObjectClass(AZStd::string_view objectClass) const { return objectClass.compare(m_className) == 0; }
//! Attempts to initialize node with a BehaviorContext BehaviorMethod
//! If the className is empty, then the methodName is searched on the BehaviorContext
//! If className is not empty the className is used to look for a registered BehaviorEBus in the BehaviorContext
//! and if found, the methodName is searched among the BehaviorEBus events
//! Otherwise the className is used to look for a registered BehaviorClass in the BehaviorContext
//! and if found, the methodName is searched among the BehaviorClass methods
void InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName);
//! 1) If the names match an overloaded method, including one using ExplicitOverloadInfo, then that method is used. Else:
//! 2) If the class name is empty, then search for a free method is searched for in the BehaviorContext and there is a warning if not found.
//! 3) If the class name matches an ebus, methodName is searched among the BehaviorEBus events, and there is a warning if not found.
//! 4) if the class name does NOT match an ebus, className and methodName are used to look for a registered BehaviorClass in the BehaviorContext, and there is a warning if not found.
void InitializeBehaviorMethod(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus);
void InitializeClass(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName);
void InitializeClass(const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus);
void InitializeEvent(const NamespacePath& namespaces, AZStd::string_view busName, AZStd::string_view eventName);
@@ -126,6 +125,8 @@ namespace ScriptCanvas
virtual DynamicDataType GetOverloadedOutputType(size_t resultIndex) const;
PropertyStatus GetPropertyStatus() const;
protected:
void ConfigureMethod(const AZ::BehaviorMethod& method, const AZ::BehaviorClass* bcClass);
@@ -637,6 +637,16 @@ namespace ScriptCanvas
{
WriteGlobalPropertyRead(execution);
}
else if (Grammar::IsClassPropertyRead(execution))
{
WriteClassPropertyRead(execution);
m_dotLua.WriteNewLine();
}
else if (Grammar::IsClassPropertyWrite(execution))
{
WriteClassPropertyWrite(execution);
m_dotLua.WriteNewLine();
}
else
{
const bool isNullCheckRequired = Grammar::IsFunctionCallNullCheckRequired(execution);
@@ -1208,6 +1218,19 @@ namespace ScriptCanvas
TranslateNodeableParse();
}
void GraphToLua::WriteClassPropertyRead(Grammar::ExecutionTreeConstPtr execution)
{
WriteFunctionCallInput(execution, 0, IsFormatStringInput::No);
m_dotLua.Write(".%s", Grammar::ToIdentifier(execution->GetName()).c_str());
}
void GraphToLua::WriteClassPropertyWrite(Grammar::ExecutionTreeConstPtr execution)
{
WriteClassPropertyRead(execution);
m_dotLua.Write(" = ");
WriteFunctionCallInput(execution, 1, IsFormatStringInput::No);
}
void GraphToLua::WriteConditionalCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol, const Grammar::ExecutionChild& child, size_t index)
{
if (symbol == Grammar::Symbol::RandomSwitch)
@@ -116,6 +116,8 @@ namespace ScriptCanvas
void TranslateNodeableParse();
void TranslateStaticInitialization();
void TranslateVariableInitialization(AZStd::string_view leftValue);
void WriteClassPropertyRead(Grammar::ExecutionTreeConstPtr);
void WriteClassPropertyWrite(Grammar::ExecutionTreeConstPtr);
void WriteConditionalCaseSwitch(Grammar::ExecutionTreeConstPtr execution, Grammar::Symbol symbol, const Grammar::ExecutionChild& child, size_t index);
enum class IsLeadingCommaRequired { No, Yes };
void WriteConstructionArgs();
@@ -61,7 +61,7 @@ namespace ScriptCanvas
return { typeID };
}
bool BehaviorContextUtils::FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, [[maybe_unused]] AZStd::string_view className, [[maybe_unused]] AZStd::string_view methodName, [[maybe_unused]] AZStd::string* outPrettyClassName, [[maybe_unused]] bool warnOnMissing)
bool BehaviorContextUtils::FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, [[maybe_unused]] AZStd::string_view className, [[maybe_unused]] AZStd::string_view methodName, PropertyStatus propertyStatus, [[maybe_unused]] AZStd::string* outPrettyClassName, [[maybe_unused]] bool warnOnMissing)
{
AZ::BehaviorContext* behaviorContext(nullptr);
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
@@ -81,16 +81,36 @@ namespace ScriptCanvas
const AZ::BehaviorClass* behaviorClass(classIter->second);
AZ_Assert(behaviorClass, "BehaviorContext Class entry %s has no class pointer", className.data());
const auto methodIter(behaviorClass->m_methods.find(methodName.data()));
if (methodIter == behaviorClass->m_methods.end())
AZ::BehaviorMethod* method{};
if (propertyStatus == PropertyStatus::None)
{
AZ_Warning("Script Canvas", !warnOnMissing, "No method by name of %s found in BehaviorContext class %s", methodName.data(), className.data());
return false;
const auto methodIter(behaviorClass->m_methods.find(methodName.data()));
if (methodIter != behaviorClass->m_methods.end())
{
method = methodIter->second;
propertyStatus = PropertyStatus::None;
}
else
{
AZ_Warning("Script Canvas", !warnOnMissing, "No method by name of %s found in BehaviorContext class %s", methodName.data(), className.data());
}
}
else
{
const auto propertyIter(behaviorClass->m_properties.find(methodName.data()));
if (propertyIter == behaviorClass->m_properties.end())
{
AZ_Warning("Script Canvas", !warnOnMissing, "No property by name of %s found in BehaviorContext class %s", methodName.data(), className.data());
return false;
}
method = propertyStatus == PropertyStatus::Getter ? propertyIter->second->m_getter : propertyIter->second->m_setter;
}
// this argument is the first argument...so perhaps remove the distinction between class and member functions, since it probably won't follow polymorphism
// if it will, keep the distinction, and add the first argument separately
AZ::BehaviorMethod* method(methodIter->second);
if (!method)
{
AZ_Warning("Script Canvas", !warnOnMissing, "BehaviorContext Method entry %s has no method pointer", methodName.data());
@@ -21,7 +21,7 @@ namespace ScriptCanvas
class BehaviorContextUtils
{
public:
static bool FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, AZStd::string* outPrettyClassName = nullptr, bool warnOnMissing = true);
static bool FindClass(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, PropertyStatus propertyStatus = PropertyStatus::None, AZStd::string* outPrettyClassName = nullptr, bool warnOnMissing = true);
static bool FindEBus(const AZ::BehaviorEBus*& outEBus, AZStd::string_view ebusName, bool warnOnMissing = true);
static bool FindExplicitOverload(const AZ::BehaviorMethod*& outMethod, const AZ::BehaviorClass*& outClass, AZStd::string_view className, AZStd::string_view methodName, AZStd::string* outPrettyClassName = nullptr);
static AZStd::string FindExposedMethodName(const AZ::BehaviorMethod& method, const AZ::BehaviorClass* behaviorClass);
@@ -58,7 +58,7 @@ namespace ScriptCanvas
}
else
{
return ConstructMethodNodeIdentifier(methodNode->GetRawMethodClassName(), methodNode->GetName());
return ConstructMethodNodeIdentifier(methodNode->GetRawMethodClassName(), methodNode->GetName(), methodNode->GetPropertyStatus());
}
}
else if (auto ebusNode = azrtti_cast<const Nodes::Core::EBusEventHandler*>(scriptCanvasNode))
@@ -158,13 +158,14 @@ namespace ScriptCanvas
return resultHash;
}
NodeTypeIdentifier NodeUtils::ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName)
NodeTypeIdentifier NodeUtils::ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName, ScriptCanvas::PropertyStatus propertyStatus)
{
NodeTypeIdentifier resultHash = 0;
AZStd::hash_combine(resultHash, AZStd::hash<AZ::Uuid>()(azrtti_typeid<ScriptCanvas::Nodes::Core::Method>()));
AZStd::hash_combine(resultHash, AZStd::hash<AZStd::string_view>()(methodClass));
AZStd::hash_combine(resultHash, AZStd::hash<AZStd::string>()(methodName));
AZStd::hash_combine(resultHash, AZStd::hash<AZ::u8>()(static_cast<AZ::u8>(propertyStatus)));
return resultHash;
}
@@ -253,7 +254,7 @@ namespace ScriptCanvas
if (auto* method = azrtti_cast<ScriptCanvas::Nodes::Core::Method*>(node))
{
ScriptCanvas::NamespacePath emptyNamespaces;
method->InitializeBehaviorMethod(emptyNamespaces, config.m_className, config.m_methodName);
method->InitializeBehaviorMethod(emptyNamespaces, config.m_className, config.m_methodName, config.m_propertyStatus);
}
}
}
@@ -38,7 +38,7 @@ namespace ScriptCanvas
static NodeTypeIdentifier ConstructCustomNodeIdentifier(const AZ::Uuid& nodeId);
static NodeTypeIdentifier ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName);
static NodeTypeIdentifier ConstructMethodNodeIdentifier(AZStd::string_view methodClass, AZStd::string_view methodName, ScriptCanvas::PropertyStatus propertyStatus);
static NodeTypeIdentifier ConstructGlobalMethodNodeIdentifier(AZStd::string_view methodName);
static NodeTypeIdentifier ConstructMethodOverloadedNodeIdentifier(AZStd::string_view methodName);