Merge branch 'main' into LYN-3077

This commit is contained in:
amzn-sj
2021-04-22 10:11:13 -07:00
629 changed files with 29269 additions and 30244 deletions
@@ -394,11 +394,13 @@ namespace ScriptCanvasBuilder
int GetBuilderVersion()
{
// #functions2 remove-execution-out-hash include version from all library nodes, split fingerprint generation to relax Is Out of Data restriction when graphs only need a recompile
return static_cast<int>(BuilderVersion::Current)
+ static_cast<int>(ScriptCanvas::GrammarVersion::Current)
+ static_cast<int>(ScriptCanvas::RuntimeVersion::Current)
;
}
AZ::Outcome < AZ::Data::Asset<ScriptCanvasEditor::ScriptCanvasAsset>, AZStd::string> LoadEditorAsset(AZStd::string_view filePath)
{
AZStd::shared_ptr<AZ::Data::AssetDataStream> assetDataStream = AZStd::make_shared<AZ::Data::AssetDataStream>();
@@ -2347,7 +2347,7 @@ namespace ScriptCanvasEditor
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> addOutcome;
// #functions2 slot<->variable re-use the activeDatum, send the pointer (actually, all of the source slot information, and make a special conversion)
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(addOutcome, GetScriptCanvasId(), &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, variableDatum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(addOutcome, GetScriptCanvasId(), &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, variableDatum, true);
if (addOutcome.IsSuccess())
{
@@ -172,7 +172,7 @@ namespace ScriptCanvasEditor
{
ScriptCanvas::Datum datum = ScriptCanvas::Datum(entityId);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string > addVariableOutcome = variableManagerRequests->AddVariable(variableName, datum);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string > addVariableOutcome = variableManagerRequests->AddVariable(variableName, datum, false);
if (addVariableOutcome.IsSuccess())
{
@@ -79,15 +79,19 @@ namespace
// Create the nodes in a horizontal list at the top of the canvas.
AZ::Vector2 pos(20.0f, -100.0f);
AZ::Vector2 pos(20.0f, 20.0f);
for (const auto& index : ui->commandList->selectionModel()->selectedIndexes())
{
if (index.column() != CommandListDataModel::ColumnIndex::Command)
if (index.column() != CommandListDataModel::ColumnIndex::CommandIndex)
{
continue;
}
AZ::Uuid type = dataModel->data(index, CommandListDataModel::CustomRole::Types).value<AZ::Uuid>();
if (type.IsNull())
{
continue;
}
[[maybe_unused]] const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(type);
AZ_Assert(classData, "Failed to find ClassData for ID: %s", type.ToString<AZStd::string>().data());
@@ -115,6 +119,8 @@ namespace ScriptCanvasEditor
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataModel::CommandListDataModel([[maybe_unused]] QWidget* parent /*= nullptr*/)
{
ScriptCanvasCommandLineRequestBus::Handler::BusConnect();
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
@@ -138,12 +144,62 @@ namespace ScriptCanvasEditor
if (add)
{
m_nodeTypes.push_back(classData->m_typeId);
Entry entry;
entry.m_type = classData->m_typeId;
m_entries.emplace_back(entry);
}
}
return true;
}
);
);
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::AddCommand, "add_node", "Adds the specified node to the graph",
[serializeContext](const AZStd::vector<AZStd::string>& nodes)
{
AZ::Uuid nodeTypeToAdd = AZ::Uuid::CreateNull();
if (nodes.size() > 0)
{
const AZStd::string& nodeName = *(nodes.begin());
serializeContext->EnumerateDerived<ScriptCanvas::Node>(
[&nodeName, &nodeTypeToAdd](const AZ::SerializeContext::ClassData* classData, [[maybe_unused]] const AZ::Uuid& classUuid) -> bool
{
if (classData && classData->m_editData)
{
if (nodeName.compare(classData->m_name) == 0)
{
nodeTypeToAdd = classData->m_typeId;
}
}
return true;
}
);
if (!nodeTypeToAdd.IsNull())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(scriptCanvasId, &ScriptCanvasEditor::GeneralRequests::GetActiveScriptCanvasId);
AZ::EntityId graphCanvasGraphId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &ScriptCanvasEditor::GeneralRequests::GetActiveGraphCanvasGraphId);
if (scriptCanvasId.IsValid() && graphCanvasGraphId.IsValid())
{
ScriptCanvasEditor::Nodes::StyleConfiguration styleConfiguration;
AZ::Vector2 pos(100.0f, 20.0f);
NodeIdPair nodePair = ScriptCanvasEditor::Nodes::CreateNode(nodeTypeToAdd, scriptCanvasId, styleConfiguration);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodePair.m_graphCanvasId, pos, false);
}
}
}
}
);
}
CommandListDataModel::~CommandListDataModel()
{
ScriptCanvasCommandLineRequestBus::Handler::BusDisconnect();
}
QModelIndex CommandListDataModel::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const
@@ -162,7 +218,7 @@ namespace ScriptCanvasEditor
int CommandListDataModel::rowCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
{
return static_cast<int>(m_nodeTypes.size());
return static_cast<int>(m_entries.size());
}
int CommandListDataModel::columnCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
@@ -190,19 +246,40 @@ namespace ScriptCanvasEditor
}
}
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
AZ::Uuid nodeType = m_entries[index.row()].m_type;
if (nodeType.IsNull())
{
return QVariant(QString(classData->m_name));
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
AZStd::string command = m_entries[index.row()].m_command;
const auto& entry = m_commands.find(command);
if (entry != m_commands.end())
{
return QVariant(QString(entry->second->GetDescription().c_str()));
}
}
}
if (index.column() == ColumnIndex::Description)
else
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::Trail)
{
return QVariant(QString(""));
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType))
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::TrailIndex)
{
return QVariant(QString(""));
}
}
}
}
@@ -210,25 +287,42 @@ namespace ScriptCanvasEditor
{
case CustomRole::Types:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
AZ::Uuid nodeType = m_entries[index.row()].m_type;
return QVariant::fromValue<AZ::Uuid>(nodeType);
}
break;
case CustomRole::Node:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
AZ::Uuid nodeType = m_entries[index.row()].m_type;
if (nodeType.IsNull())
{
return QVariant(QString(classData->m_name));
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
if (index.column() == ColumnIndex::Description)
else
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType))
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::DescriptionIndex)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::TrailIndex)
{
return QVariant(QString(""));
}
}
}
if (index.column() == ColumnIndex::Trail)
}
break;
case CustomRole::Commands:
{
if (index.column() == ColumnIndex::CommandIndex)
{
return QVariant(QString(""));
return QVariant(QString(m_entries[index.row()].m_command.c_str()));
}
}
break;
@@ -250,21 +344,31 @@ namespace ScriptCanvasEditor
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
for (const auto& entry : m_nodeTypes)
for (const auto& entry : m_entries)
{
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entry);
if (classData)
if (!entry.m_type.IsNull())
{
QString name = QString(classData->m_name);
if (name.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
if (const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entry.m_type))
{
return true;
QString name = QString(classData->m_name);
if (name.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
{
return true;
}
}
}
else
{
QString commandName = entry.m_command.c_str();
return (commandName.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive));
}
}
return false;
}
ScriptCanvasEditor::Widget::CommandRegistry CommandListDataModel::m_commands;
// CommandLineEdit
/////////////////////////////////////////////////////////////////////////////////////////////
@@ -335,8 +439,25 @@ namespace ScriptCanvasEditor
case Qt::Key_Return:
{
// Invoke the command
// TODO: trigger invoke
// CommandRequestBus::Broadcast(&CommandRequest::Invoke, text().toStdString().c_str());
AZStd::string commandText = text().toStdString().c_str();
AZStd::vector<AZStd::string> tokens;
AZ::StringFunc::Tokenize(commandText, tokens, " ");
if (tokens.size() == 1)
{
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::Invoke, tokens.begin()->c_str());
}
else if (tokens.size() > 1)
{
AZStd::string command = *(tokens.begin());
AZStd::vector<AZStd::string> args;
for (auto it = tokens.begin() + 1; it != tokens.end(); ++it)
{
args.push_back(*it);
}
ScriptCanvasCommandLineRequestBus::Broadcast(&ScriptCanvasCommandLineRequests::InvokeWithArguments, command.c_str(), args);
}
ResetState();
qobject_cast<QWidget*>(parent())->hide();
}
@@ -376,20 +497,29 @@ namespace ScriptCanvasEditor
// CommandListDataProxyModel
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataProxyModel::CommandListDataProxyModel(QObject* parent /*= nullptr*/)
CommandListDataProxyModel::CommandListDataProxyModel(CommandListDataModel* commandListData, QObject* parent /*= nullptr*/)
: QSortFilterProxyModel(parent)
{
QStringList commands;
setSourceModel(commandListData);
QStringList commandList;
CommandListDataModel* commandListData = new CommandListDataModel();
for (int i = 0; i < commandListData->rowCount(); ++i)
{
QModelIndex index = commandListData->index(i, CommandListDataModel::ColumnIndex::Command);
QModelIndex index = commandListData->index(i, CommandListDataModel::ColumnIndex::CommandIndex);
QString command = commandListData->data(index, CommandListDataModel::CustomRole::Node).toString();
commands.push_back(command);
commandList.push_back(command);
}
m_completer = new QCompleter(commands);
ScriptCanvasCommandLineRequests::CommandNameList commands;
ScriptCanvasCommandLineRequestBus::BroadcastResult(commands, &ScriptCanvasCommandLineRequests::GetCommands);
for (auto& command : commands)
{
QString commandName = command.first.c_str();
commandList.push_back(commandName);
}
m_completer = new QCompleter(commandList);
m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
m_completer->setCaseSensitivity(Qt::CaseInsensitive);
}
@@ -421,7 +551,7 @@ namespace ScriptCanvasEditor
}
}
QModelIndex index = dataModel->index(sourceRow, CommandListDataModel::ColumnIndex::Command);
QModelIndex index = dataModel->index(sourceRow, CommandListDataModel::ColumnIndex::CommandIndex);
QString sourceStr = dataModel->data(index).toString();
if (sourceRow > 0 && sourceStr.startsWith(m_input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
@@ -450,8 +580,7 @@ namespace ScriptCanvasEditor
ui->setupUi(this);
CommandListDataModel* commandListDataModel = new CommandListDataModel();
CommandListDataProxyModel* commandListDataProxyModel = new CommandListDataProxyModel();
commandListDataProxyModel->setSourceModel(commandListDataModel);
CommandListDataProxyModel* commandListDataProxyModel = new CommandListDataProxyModel(commandListDataModel);
ui->commandList->setModel(commandListDataProxyModel);
@@ -460,8 +589,8 @@ namespace ScriptCanvasEditor
connect(ui->commandText, &CommandLineEdit::onKeyReleased, this, &CommandLine::onEditKeyReleaseEvent);
connect(ui->commandList, &CommandLineList::onKeyReleased, this, &CommandLine::onListKeyReleaseEvent);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Command, 250);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Description, 1000);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::CommandIndex, 250);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::DescriptionIndex, 1000);
}
void CommandLine::onTextChanged(const QString& text)
@@ -25,6 +25,7 @@
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Console/Console.h>
#endif
namespace Ui
@@ -36,10 +37,49 @@ namespace ScriptCanvasEditor
{
namespace Widget
{
class Command
{
public:
using Functor = AZStd::function<void(AZStd::vector<AZStd::string>)>;
Command(const AZStd::string& name, const AZStd::string& description, Functor functor)
: m_name(name)
, m_description(description)
, m_functor(functor)
{}
void operator()(const AZStd::vector<AZStd::string>& args)
{
m_functor(args);
}
const AZStd::string& GetName() const { return m_name; }
const AZStd::string& GetDescription() const { return m_description; }
private:
AZStd::string m_name;
AZStd::string m_description;
Functor m_functor;
};
using CommandRegistry = AZStd::unordered_map<AZStd::string, AZStd::unique_ptr<Command>>;
struct ScriptCanvasCommandLineRequests : public AZ::EBusTraits
{
virtual void AddCommand(const AZStd::string commandName, const AZStd::string description, Command::Functor) = 0;
virtual void Invoke(const char* commandName) = 0;
virtual void InvokeWithArguments(const char* commandName, const AZStd::vector<AZStd::string>&) = 0;
using CommandNameList = AZStd::list<AZStd::pair<AZStd::string, AZStd::string>>;
virtual CommandNameList GetCommands() = 0;
};
using ScriptCanvasCommandLineRequestBus = AZ::EBus<ScriptCanvasCommandLineRequests>;
// TODO #lsempe: this deserves its own file
// CommandListDataModel
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandListDataModel : public QAbstractTableModel
, ScriptCanvasCommandLineRequestBus::Handler
{
Q_OBJECT
@@ -49,9 +89,9 @@ namespace ScriptCanvasEditor
enum ColumnIndex
{
Command,
Description,
Trail,
CommandIndex,
DescriptionIndex,
TrailIndex,
Count
};
@@ -65,6 +105,8 @@ namespace ScriptCanvasEditor
};
CommandListDataModel(QWidget* parent = nullptr);
~CommandListDataModel() override;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -75,10 +117,62 @@ namespace ScriptCanvasEditor
bool HasMatches(const AZStd::string& input);
struct Entry
{
AZ::Uuid m_type;
AZStd::string m_command;
Entry()
{
m_type = AZ::Uuid::CreateNull();
}
};
protected:
AZStd::vector<AZ::Uuid> m_nodeTypes;
AZStd::vector<Entry> m_entries;
static CommandRegistry m_commands;
void AddCommand(const AZStd::string commandName, const AZStd::string description, Command::Functor f) override
{
if (m_commands.find(commandName) == m_commands.end())
{
m_commands[commandName] = AZStd::make_unique<Command>(commandName, description, f);
Entry entry;
entry.m_command = commandName;
entry.m_type = AZ::Uuid::CreateNull();
m_entries.emplace_back(entry);
}
}
void Invoke(const char* commandName) override
{
auto command = m_commands.find(commandName);
if (command != m_commands.end())
{
command->second->operator()({});
}
}
void InvokeWithArguments(const char* commandName, const AZStd::vector<AZStd::string>& args) override
{
auto command = m_commands.find(commandName);
if (command != m_commands.end())
{
command->second->operator()(args);
}
}
ScriptCanvasCommandLineRequests::CommandNameList GetCommands() override
{
ScriptCanvasCommandLineRequests::CommandNameList commands;
for (auto& command : m_commands)
{
commands.push_back(AZStd::make_pair(command.second->GetName(), command.second->GetDescription()));
}
return commands;
}
};
class CommandListDataProxyModel : public QSortFilterProxyModel
@@ -88,7 +182,7 @@ namespace ScriptCanvasEditor
public:
AZ_CLASS_ALLOCATOR(CommandListDataProxyModel, AZ::SystemAllocator, 0);
CommandListDataProxyModel(QObject* parent = nullptr);
CommandListDataProxyModel(CommandListDataModel* commandListData, QObject* parent = nullptr);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
@@ -168,4 +262,4 @@ namespace ScriptCanvasEditor
AZStd::unique_ptr<Ui::CommandLine> ui;
};
}
}
}
@@ -41,11 +41,11 @@ namespace ScriptCanvasEditor
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateFunctionMimeEvent, CreateNodeMimeEvent>()
->Version(4)
->Version(5)
->Field("AssetId", &CreateFunctionMimeEvent::m_assetId)
->Field("sourceId", &CreateFunctionMimeEvent::m_sourceId)
;
}
}
CreateFunctionMimeEvent::CreateFunctionMimeEvent(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const ScriptCanvas::Grammar::FunctionSourceId& sourceId)
@@ -57,7 +57,7 @@ namespace
{
if (excludeAttributeData)
{
AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly | AZ::ScriptCanvasAttributes::VariableCreationForbidden;
AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly;
if (typeId == AzToolsFramework::Components::EditorComponentBase::TYPEINFO_Uuid())
{
@@ -143,11 +143,6 @@ namespace
{
return;
}
if (!ScriptCanvas::Data::IsAllowedBehaviorClassVariableType(behaviorClass->m_typeId))
{
return;
}
}
const auto isExposableOutcome = ScriptCanvas::IsExposable(method);
@@ -479,38 +474,24 @@ namespace
continue;
}
// Only bind Behavior Classes marked with the Scope type of Launcher
if (auto excludeFromPointer = AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes))
{
AZ::Script::Attributes::ExcludeFlags excludeFlags{};
AZ::AttributeReader(nullptr, excludeFromPointer).Read<AZ::Script::Attributes::ExcludeFlags>(excludeFlags);
if ((excludeFlags & (AZ::Script::Attributes::ExcludeFlags::List | AZ::Script::Attributes::ExcludeFlags::ListOnly)) != 0)
{
continue;
}
}
if (!AZ::Internal::IsInScope(behaviorClass->m_attributes, AZ::Script::Attributes::ScopeFlags::Launcher))
{
continue; // skip this class
continue;
}
// Objects and Object methods
{
bool canCreate = serializeContext->FindClassData(behaviorClass->m_typeId) != nullptr &&
!HasAttribute(behaviorClass, AZ::ScriptCanvasAttributes::VariableCreationForbidden);
// In order to create variables, the class must have full memory support
canCreate = canCreate &&
(behaviorClass->m_allocate
&& behaviorClass->m_cloner
&& behaviorClass->m_mover
&& behaviorClass->m_destructor
&& behaviorClass->m_deallocate);
if (canCreate)
{
// Do not allow variable creation for data that derives from AZ::Component
for (auto base : behaviorClass->m_baseClasses)
{
if (AZ::Component::TYPEINFO_Uuid() == base)
{
canCreate = false;
break;
}
}
}
AZStd::string categoryPath;
AZStd::string translationContext = ScriptCanvasEditor::TranslationHelper::GetContextName(ScriptCanvasEditor::TranslationContextGroup::ClassMethod, behaviorClass->m_name);
@@ -530,17 +511,14 @@ namespace
}
}
if (canCreate)
{
auto dataRegistry = ScriptCanvas::GetDataRegistry();
ScriptCanvas::Data::Type type = dataRegistry->m_typeIdTraitMap[ScriptCanvas::Data::eType::BehaviorContextObject].m_dataTraits.GetSCType(behaviorClass->m_typeId);
auto dataRegistry = ScriptCanvas::GetDataRegistry();
ScriptCanvas::Data::Type type = dataRegistry->m_typeIdTraitMap[ScriptCanvas::Data::eType::BehaviorContextObject].m_dataTraits.GetSCType(behaviorClass->m_typeId);
if (type.IsValid())
if (type.IsValid())
{
if (dataRegistry->m_creatableTypes.contains(type))
{
if (!AZ::FindAttribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, behaviorClass->m_attributes))
{
ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type);
}
ScriptCanvasEditor::VariablePaletteRequestBus::Broadcast(&ScriptCanvasEditor::VariablePaletteRequests::RegisterVariableType, type);
}
}
@@ -578,6 +556,14 @@ namespace
categoryPath.append(displayName.c_str());
}
for (auto property : behaviorClass->m_properties)
{
if (property.second->m_setter)
{
RegisterMethod(nodePaletteModel, behaviorContext, categoryPath, behaviorClass, property.first, *property.second->m_setter, behaviorClass->IsMethodOverloaded(property.first));
}
}
for (auto methodIter : behaviorClass->m_methods)
{
if (!IsExplicitOverload(*methodIter.second))
@@ -1011,7 +1011,7 @@ namespace ScriptCanvasEditor
ScriptCanvas::Datum datum(variableType, ScriptCanvas::Datum::eOriginality::Original);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> outcome = AZ::Failure(AZStd::string());
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, varName, datum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_activeGraphIds.scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, varName, datum, false);
if (outcome.IsSuccess())
{
@@ -41,6 +41,7 @@
#include <ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
namespace ScriptCanvasEditor
{
@@ -538,7 +539,24 @@ namespace ScriptCanvasEditor
}
else if (index.column() == ColumnIndex::Scope)
{
// Scope is not changed by users
ScriptCanvas::GraphVariable* graphVariable = nullptr;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(graphVariable, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::FindVariableById, varId.m_identifier);
if (graphVariable)
{
QString comboBoxValue = value.toString();
if (!comboBoxValue.isEmpty())
{
AZStd::string scopeLabel = ScriptCanvas::VariableFlags::GetScopeDisplayLabel(graphVariable->GetScope());
if (scopeLabel.compare(comboBoxValue.toUtf8().data()) != 0)
{
modifiedData = true;
graphVariable->SetScope(ScriptCanvas::VariableFlags::GetScopeFromLabel(comboBoxValue.toUtf8().data()));
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::Refresh_EntireTree);
}
}
}
}
else if (index.column() == ColumnIndex::InitialValueSource)
{
@@ -607,8 +625,17 @@ namespace ScriptCanvasEditor
}
else if (index.column() == ColumnIndex::Scope)
{
ScriptCanvas::GraphScopedVariableId varId = FindScopedVariableIdForIndex(index);
ScriptCanvas::GraphVariable* graphVariable = nullptr;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(graphVariable, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::FindVariableById, varId.m_identifier);
if (graphVariable->GetScope() != ScriptCanvas::VariableFlags::Scope::FunctionReadOnly)
{
itemFlags |= Qt::ItemIsEditable;
}
}
else if (index.column() == ColumnIndex::InitialValueSource)
{
itemFlags |= Qt::ItemIsEditable;
@@ -73,6 +73,8 @@ namespace ScriptCanvasEditor
{
ui->setupUi(this);
ui->variablePalette->SetActiveScene(scriptCanvasId);
ui->searchFilter->setClearButtonEnabled(true);
QObject::connect(ui->searchFilter, &QLineEdit::textChanged, this, &SlotTypeSelectorWidget::OnQuickFilterChanged);
QObject::connect(ui->slotName, &QLineEdit::returnPressed, this, &SlotTypeSelectorWidget::OnReturnPressed);
@@ -812,7 +812,7 @@ namespace ScriptCanvasEditor
ScriptCanvas::Datum datum(varType, ScriptCanvas::Datum::eOriginality::Original);
AZ::Outcome<ScriptCanvas::VariableId, AZStd::string> outcome = AZ::Failure(AZStd::string());
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, datum);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, datum, false);
AZ_Warning("VariablePanel", outcome.IsSuccess(), "Could not create new variable: %s", outcome.GetError().c_str());
GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasId);
@@ -122,8 +122,8 @@ namespace ScriptCanvasEditor
for (const AZ::Uuid& objectId : objectTypes)
{
// Verify whether this is an allowed BC variable type
if (!ScriptCanvas::Data::IsAllowedBehaviorClassVariableType(objectId))
ScriptCanvas::Data::Type type = dataRegistry->m_typeIdTraitMap[ScriptCanvas::Data::eType::BehaviorContextObject].m_dataTraits.GetSCType(objectId);
if (!type.IsValid() || !dataRegistry->m_creatableTypes.contains(type))
{
continue;
}
@@ -595,7 +595,6 @@ namespace ScriptCanvasEditor
m_commandLine = new Widget::CommandLine(this);
m_commandLine->setBaseSize(QSize(size().width(), m_commandLine->size().height()));
m_commandLine->setObjectName("CommandLine");
m_commandLine->hide();
m_layout->addWidget(m_commandLine);
m_layout->addWidget(m_emptyCanvas);
@@ -244,7 +244,7 @@
<bool>false</bool>
</property>
<property name="visible">
<bool>false</bool>
<bool>true</bool>
</property>
</action>
<action name="action_ViewNodePalette">
@@ -66,7 +66,7 @@ namespace {{attribute_Namespace}}
{% set deprecationUuid = Class.attrib['DeprecationUUID'] %}
// The following will be injected directly into the source header file for which AzCodeGenerator is being run.
// The following will be injected directly into the source header file for which AZ AutoGen is being run.
// You must #include the generated header into the source header
#define SCRIPTCANVAS_NODE_{{ className }} \
public: \
@@ -158,7 +158,7 @@ return {{returnNames[0]}};
{% endfor %}
{# ExecutionOuts #}
// ExecutionOuts begin
{{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName )}}
{{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName)}}
// ExecutionOuts end
{# Reflect #}
void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
@@ -307,41 +307,49 @@ AZStd::tuple<{{returns|join(", ")}}>
{% for executionOut in Class.findall('Output') %}
{{ ExecutionOutDeclaration(Class, executionOut) }}
{%- endfor %}
size_t GetRequiredOutCount() const override;
{% endmacro %}
{% macro ExecutionBranchDefinition(Class, qualifiedName, executionOut) %}
{% macro ExecutionBranchDefinition(Class, qualifiedName, executionOut, outIndexBranch) %}
{% set outName = CleanName(executionOut.attrib['Name']) %}
{% set returns = executionOut.findall('Parameter') %}
{% set params = executionOut.findall('Return') %}
void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinition(returns)}}{{ExecutionOutParameterDefinition(returns, params)}}) {
void {{qualifiedName}}::Call{{outName}}({{ExecutionOutReturnDefinition(returns)}}{{ExecutionOutParameterDefinition(returns, params)}}) {
{% if returns|length() == 0 %}
ExecutionOut(AZ_CRC_CE("{{ executionOut.attrib['Name'] }}"){% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}} {% endfor %}
ExecutionOut({{ outIndexBranch }}{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}}{% endfor %}
{% else %}
OutResult(AZ_CRC_CE("{{ executionOut.attrib['Name'] }}"), result{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}} {% endfor %}
{% endif -%});
ExecutionOutResult({{ outIndexBranch }}, result{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}}{% endfor %}
{% endif -%}); // {{ executionOut.attrib['Name'] }}
}
{% endmacro %}
{% macro ExecutionOutDefinition(Class, qualifiedName, executionOut) %}
{% macro ExecutionOutDefinition(Class, qualifiedName, executionOut, outIndexLatent) %}
{% set outName = CleanName(executionOut.attrib['Name']) %}
{% set returns = executionOut.findall('Return') %}
{% set params = executionOut.findall('Parameter') %}
void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinition(returns)}}{{ExecutionOutParameterDefinition(returns, params)}}) {
{% if returns|length() == 0 %}
ExecutionOut(AZ_CRC_CE("{{ executionOut.attrib['Name'] }}"){% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}} {% endfor %}
ExecutionOut({{ outIndexLatent }}{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}}{% endfor %}
{% else %}
OutResult(AZ_CRC_CE("{{ executionOut.attrib['Name'] }}"), result{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}} {% endfor %}
{% endif -%} );
ExecutionOutResult({{ outIndexLatent }}, result{% for parameter in params %}, {{CleanName(parameter.attrib['Name'])}}{% endfor %}
{% endif -%}); // {{ executionOut.attrib['Name'] }}
}
{% endmacro %}
{% macro ExecutionOutDefinitions(Class, qualifiedName) %}
{% set branches = [] %}
{% for method in Class.findall('Input') %}
{%- for branch in method.findall('Branch') %}
{{ ExecutionBranchDefinition(Class, qualifiedName, branch) }}
{% if branches.append(branch) %}{% endif %}
{%- endfor %}
{% endfor %}
{%- for executionOut in Class.findall('Output') -%}
{{ ExecutionOutDefinition(Class, qualifiedName, executionOut) }}
{% endfor %}
{%- for branch in branches -%}
{{ ExecutionBranchDefinition(Class, qualifiedName, branch, loop.index0) }}
{%- endfor %}
{%- for executionOut in Class.findall('Output') -%}
{{ ExecutionOutDefinition(Class, qualifiedName, executionOut, loop.index0 + branches|length) }}
{%- endfor %}
size_t {{qualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }
{% endmacro %}
@@ -208,7 +208,13 @@ namespace ScriptCanvas
static const t_Value* Help(Datum& datum)
{
static_assert(!AZStd::is_pointer<t_Value>::value, "no pointer types in the Datum::GetAsHelper<t_Value, false>");
if (datum.m_type.GetType() == Data::eType::BehaviorContextObject)
if (datum.m_storage.empty())
{
// rare, but can be caused by removals or problems with reflection to BehaviorContext, so must be checked
return nullptr;
}
else if (datum.m_type.GetType() == Data::eType::BehaviorContextObject)
{
return (*AZStd::any_cast<BehaviorContextObjectPtr>(&datum.m_storage))->CastConst<t_Value>();
}
@@ -88,17 +88,7 @@ namespace ScriptCanvas
void EBusHandler::InitializeEBusHandling(AZStd::string_view busName, AZ::BehaviorContext* behaviorContext)
{
CreateHandler(busName, behaviorContext);
const AZ::BehaviorEBusHandler::EventArray& events = m_handler->GetEvents();
AZStd::vector<AZ::Crc32> eventKeys;
eventKeys.reserve(events.size());
for (int eventIndex(0); eventIndex < events.size(); ++eventIndex)
{
eventKeys.push_back(eventIndex);
}
InitializeExecutionOuts(eventKeys);
InitializeExecutionOuts(m_handler->GetEvents().size());
}
bool EBusHandler::IsConnected() const
@@ -137,7 +127,7 @@ namespace ScriptCanvas
void EBusHandler::OnEvent(const char* /*eventName*/, const int eventIndex, AZ::BehaviorValueParameter* result, const int numParameters, AZ::BehaviorValueParameter* parameters)
{
CallOut(AZ::Crc32(eventIndex), result, parameters, numParameters);
CallOut(eventIndex, result, parameters, numParameters);
}
void EBusHandler::Reflect(AZ::ReflectContext* reflectContext)
@@ -57,7 +57,7 @@ namespace ScriptCanvas
for (size_t resultIndex = 0; resultIndex < unpackedTypes.size(); ++resultIndex)
{
const Data::Type outputType(Data::FromAZType(unpackedTypes[resultIndex]));
const Data::Type outputType = (unpackedTypes.size() == 1 && AZ::BehaviorContextHelper::IsStringParameter(*result)) ? Data::Type::String() : Data::FromAZType(unpackedTypes[resultIndex]);
const AZStd::string resultSlotName(AZStd::string::format("Result: %s", Data::GetName(outputType).data()));
SlotId addedSlotId;
@@ -3155,6 +3155,11 @@ namespace ScriptCanvas
return {};
}
AZStd::optional<size_t> Node::GetEventIndex([[maybe_unused]] AZStd::string eventName) const
{
return AZStd::nullopt;
}
AZStd::vector<SlotId> Node::GetEventSlotIds() const
{
return {};
@@ -3404,6 +3409,45 @@ namespace ScriptCanvas
return GetSlotByName("True");
}
size_t Node::GetOutIndex(const Slot& slot) const
{
size_t index = 0;
auto slotId = slot.GetId();
if (auto map = GetSlotExecutionMap())
{
auto& ins = map->GetIns();
for (auto& in : ins)
{
for (auto& out : in.outs)
{
// only count branches
if (in.outs.size() > 1)
{
if (out.slotId == slotId)
{
return index;
}
++index;
}
}
}
for (auto& latent : map->GetLatents())
{
if (latent.slotId == slotId)
{
return index;
}
++index;
}
}
return std::numeric_limits<size_t>::max();
}
AZ::Outcome<AZStd::string> Node::GetInternalOutKey(const Slot& slot) const
{
if (auto map = GetSlotExecutionMap())
@@ -700,6 +700,8 @@ namespace ScriptCanvas
// override if necessary, usually only when the node's execution topology dramatically alters at edit-time in a way that is not generally parseable
ConstSlotsOutcome GetSlotsInExecutionThreadByType(const Slot& executionSlot, CombinedSlotType targetSlotType, const Slot* executionChildSlot = nullptr) const;
size_t GetOutIndex(const Slot& slot) const;
// override if necessary, only used by NodeableNodes which can hide branched outs and rename them later
virtual AZ::Outcome<AZStd::string> GetInternalOutKey(const Slot& slot) const;
@@ -751,6 +753,8 @@ namespace ScriptCanvas
virtual AZStd::string GetEBusName() const;
virtual AZStd::optional<size_t> GetEventIndex(AZStd::string eventName) const;
virtual AZStd::vector<SlotId> GetEventSlotIds() const;
virtual AZStd::vector<SlotId> GetNonEventSlotIds() const;
@@ -19,7 +19,7 @@
namespace NodeableOutCpp
{
void NoOp(AZ::BehaviorValueParameter* /*result*/, AZ::BehaviorValueParameter* /*arguments*/, int /*numArguments*/) {}
void NoOp([[maybe_unused]] AZ::BehaviorValueParameter*, [[maybe_unused]] AZ::BehaviorValueParameter*, [[maybe_unused]] int) {}
}
namespace ScriptCanvas
@@ -36,14 +36,14 @@ namespace ScriptCanvas
{}
#if !defined(RELEASE)
void Nodeable::CallOut(const AZ::Crc32 key, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const
void Nodeable::CallOut(size_t index, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const
{
GetExecutionOutChecked(key)(resultBVP, argsBVPs, numArguments);
GetExecutionOutChecked(index)(resultBVP, argsBVPs, numArguments);
}
#else
void Nodeable::CallOut(const AZ::Crc32 key, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const
void Nodeable::CallOut(size_t index, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const
{
GetExecutionOut(key)(resultBVP, argsBVPs, numArguments);
GetExecutionOut(index)(resultBVP, argsBVPs, numArguments);
}
#endif // !defined(RELEASE)
@@ -62,11 +62,6 @@ namespace ScriptCanvas
return m_executionState->GetEntityId();
}
AZ::EntityId Nodeable::GetScriptCanvasId() const
{
return m_executionState->GetScriptCanvasId();
}
void Nodeable::Reflect(AZ::ReflectContext* reflectContext)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
@@ -77,9 +72,9 @@ namespace ScriptCanvas
{
editContext->Class<Nodeable>("Nodeable", "Nodeable")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, false)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
@@ -92,33 +87,45 @@ namespace ScriptCanvas
->Constructor<ExecutionStateWeakPtr>()
->Method("Deactivate", &Nodeable::Deactivate)
->Method("InitializeExecutionState", &Nodeable::InitializeExecutionState)
->Method("InitializeExecutionOuts", &Nodeable::InitializeExecutionOuts)
->Method("InitializeExecutionOutByRequiredCount", &Nodeable::InitializeExecutionOutByRequiredCount)
->Method("IsActive", &Nodeable::IsActive)
;
}
}
const FunctorOut& Nodeable::GetExecutionOut(AZ::Crc32 key) const
const FunctorOut& Nodeable::GetExecutionOut(size_t index) const
{
auto iter = m_outs.find(key);
AZ_Assert(iter != m_outs.end(), "no out registered for key: %d", key);
AZ_Assert(iter->second, "null execution methods are not allowed, key: %d", key);
return iter->second;
AZ_Assert(index < m_outs.size(), "index out of range in Nodeable::m_outs");
auto& iter = m_outs[index];
AZ_Assert(iter, "null execution methods are not allowed, index: %zu", index);
return iter;
}
const FunctorOut& Nodeable::GetExecutionOutChecked(AZ::Crc32 key) const
const FunctorOut& Nodeable::GetExecutionOutChecked(size_t index) const
{
auto iter = m_outs.find(key);
if (iter == m_outs.end())
{
return m_noOpFunctor;
}
else if (!iter->second)
if (index >= m_outs.size() && m_outs[index])
{
return m_noOpFunctor;
}
return iter->second;
return m_outs[index];
}
AZ::EntityId Nodeable::GetScriptCanvasId() const
{
return m_executionState->GetScriptCanvasId();
}
void Nodeable::InitializeExecutionOuts(size_t count)
{
m_outs.resize(count, m_noOpFunctor);
}
void Nodeable::InitializeExecutionOutByRequiredCount()
{
InitializeExecutionOuts(GetRequiredOutCount());
}
void Nodeable::InitializeExecutionState(ExecutionState* executionState)
@@ -126,37 +133,23 @@ namespace ScriptCanvas
AZ_Assert(executionState != nullptr, "execution state for nodeable must not be nullptr");
AZ_Assert(m_executionState == nullptr, "execution state already initialized");
m_executionState = executionState->WeakFromThis();
OnInitializeExecutionState();
}
void Nodeable::InitializeExecutionOuts(const AZ::Crc32* begin, const AZ::Crc32* end)
void Nodeable::SetExecutionOut(size_t index, FunctorOut&& out)
{
m_outs.reserve(end - begin);
for (; begin != end; ++begin)
{
SetExecutionOut(*begin, AZStd::move(FunctorOut(&NodeableOutCpp::NoOp)));
}
AZ_Assert(out, "null executions methods are not allowed, index: %zu", index);
m_outs[index] = AZStd::move(out);
}
void Nodeable::InitializeExecutionOuts(const AZStd::vector<AZ::Crc32>& keys)
{
InitializeExecutionOuts(keys.begin(), keys.end());
}
void Nodeable::SetExecutionOut(AZ::Crc32 key, FunctorOut&& out)
{
AZ_Assert(out, "null executions methods are not allowed, key: %d", key);
m_outs[key] = AZStd::move(out);
}
void Nodeable::SetExecutionOutChecked(AZ::Crc32 key, FunctorOut&& out)
void Nodeable::SetExecutionOutChecked(size_t index, FunctorOut&& out)
{
if (!out)
{
AZ_Error("ScriptCanvas", false, "null executions methods are not allowed, key: %d", key);
AZ_Error("ScriptCanvas", false, "null executions methods are not allowed, index: %zu", index);
return;
}
SetExecutionOut(key, AZStd::move(out));
SetExecutionOut(index, AZStd::move(out));
}
}
@@ -29,6 +29,23 @@ namespace ScriptCanvas
class SubgraphInterface;
}
/*
Note: Many parts of AzAutoGen, compilation, and runtime depend on the order of declaration and addition of slots.
The display order can be manipulated in the editor, but it will always just be a change of view.
Whenever in doubt, this is the order, in pseudo code
for in : Ins do
somethingOrdered(in)
for branch : in.Branches do
somethingOrdered(branch)
end
end
for out : Outs do
somethingOrdered(out)
end
*/
// derive from this to make an object that when wrapped with a NodeableNode can be instantly turned into a node that is easily embedded in graphs,
// and easily compiled in
class Nodeable
@@ -40,21 +57,23 @@ namespace ScriptCanvas
// reflect nodeable class API
static void Reflect(AZ::ReflectContext* reflectContext);
// the run-time constructor for non-EBus handlers
Nodeable();
// this constructor is used by EBus handlers only
Nodeable(ExecutionStateWeakPtr executionState);
virtual ~Nodeable() = default;
void CallOut(const AZ::Crc32 key, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const;
void CallOut(size_t index, AZ::BehaviorValueParameter* resultBVP, AZ::BehaviorValueParameter* argsBVPs, int numArguments) const;
AZ::Data::AssetId GetAssetId() const;
AZ::EntityId GetEntityId() const;
const Execution::FunctorOut& GetExecutionOut(AZ::Crc32 key) const;
const Execution::FunctorOut& GetExecutionOut(size_t index) const;
const Execution::FunctorOut& GetExecutionOutChecked(AZ::Crc32 key) const;
const Execution::FunctorOut& GetExecutionOutChecked(size_t index) const;
virtual NodePropertyInterface* GetPropertyInterface(AZ::Crc32 /*propertyId*/) { return nullptr; }
@@ -66,98 +85,97 @@ namespace ScriptCanvas
// any would only be good if graphs could opt into it, and execution slots could annotate changing activity level
virtual bool IsActive() const { return false; }
void InitializeExecutionOuts(const AZ::Crc32* begin, const AZ::Crc32* end);
void InitializeExecutionOuts(size_t count);
void InitializeExecutionOuts(const AZStd::vector<AZ::Crc32>& keys);
void SetExecutionOut(size_t index, Execution::FunctorOut&& out);
void SetExecutionOut(AZ::Crc32 key, Execution::FunctorOut&& out);
void SetExecutionOutChecked(AZ::Crc32 key, Execution::FunctorOut&& out);
void SetExecutionOutChecked(size_t index, Execution::FunctorOut&& out);
protected:
void InitializeExecutionOutByRequiredCount();
void InitializeExecutionState(ExecutionState* executionState);
virtual void OnInitializeExecutionState() {}
virtual void OnDeactivate() {}
// all of these hooks are known at compile time, so no branching
// we will need with and without result calls for each time for method
// methods with result but no result requested, etc
template<typename t_Return>
void OutResult(const AZ::Crc32 key, t_Return& result) const
{
// this is correct, it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
AZ::BehaviorValueParameter resultBVP(&result);
CallOut(key, &resultBVP, nullptr, 0);
#if !defined(RELEASE)
if (!resultBVP.GetAsUnsafe<t_Return>())
{
AZ_Error("ScriptCanvas", false, "%s:CallOut(%u) failed to provide a useable result", TYPEINFO_Name(), (AZ::u32)key);
return;
}
#endif
result = *resultBVP.GetAsUnsafe<t_Return>();
}
virtual size_t GetRequiredOutCount() const { return 0; }
// Required to decay array type to pointer type
template<typename T>
using decay_array = AZStd::conditional_t<AZStd::is_array_v<AZStd::remove_reference_t<T>>, std::remove_extent_t<AZStd::remove_reference_t<T>>*, T&&>;
template<typename t_Return, typename... t_Args>
void OutResult(const AZ::Crc32 key, t_Return& result, t_Args&&... args) const
// all of these hooks are known at compile time, so no branching
// we will need with and without result calls for each type of method
// methods with result but no result requested, etc
template<typename... t_Args>
void ExecutionOut(size_t index, t_Args&&... args) const
{
// this is correct, it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
// it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
AZStd::tuple<decay_array<t_Args>...> lvalueWrapper(AZStd::forward<t_Args>(args)...);
using BVPReserveArray = AZStd::array<AZ::BehaviorValueParameter, sizeof...(args)>;
auto MakeBVPArrayFunction = [](auto&&... element)
{
return BVPReserveArray{ {AZ::BehaviorValueParameter{&element}...} };
};
BVPReserveArray argsBVPs = AZStd::apply(MakeBVPArrayFunction, lvalueWrapper);
AZ::BehaviorValueParameter resultBVP(&result);
CallOut(key, &resultBVP, argsBVPs.data(), sizeof...(t_Args));
BVPReserveArray argsBVPs = AZStd::apply(MakeBVPArrayFunction, lvalueWrapper);
CallOut(index, nullptr, argsBVPs.data(), sizeof...(t_Args));
}
void ExecutionOut(size_t index) const
{
// it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
CallOut(index, nullptr, nullptr, 0);
}
template<typename t_Return>
void ExecutionOutResult(size_t index, t_Return& result) const
{
// It is up to the FunctorOut referenced by the index to decide what to do with these params (whether to modify or handle strings differently)
AZ::BehaviorValueParameter resultBVP(&result);
CallOut(index, &resultBVP, nullptr, 0);
#if !defined(RELEASE)
if (!resultBVP.GetAsUnsafe<t_Return>())
{
AZ_Error("ScriptCanvas", false, "%s:CallOut(%u) failed to provide a useable result", TYPEINFO_Name(), (AZ::u32)key);
AZ_Error("ScriptCanvas", false, "%s:CallOut(%zu) failed to provide a useable result", TYPEINFO_Name(), index);
return;
}
#endif
result = *resultBVP.GetAsUnsafe<t_Return>();
}
template<typename... t_Args>
void ExecutionOut(const AZ::Crc32 key, t_Args&&... args) const
template<typename t_Return, typename... t_Args>
void ExecutionOutResult(size_t index, t_Return& result, t_Args&&... args) const
{
// this is correct, it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
// it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
AZStd::tuple<decay_array<t_Args>...> lvalueWrapper(AZStd::forward<t_Args>(args)...);
using BVPReserveArray = AZStd::array<AZ::BehaviorValueParameter, sizeof...(args)>;
auto MakeBVPArrayFunction = [](auto&&... element)
{
return BVPReserveArray{ {AZ::BehaviorValueParameter{&element}...} };
};
BVPReserveArray argsBVPs = AZStd::apply(MakeBVPArrayFunction, lvalueWrapper);
AZ::BehaviorValueParameter resultBVP(&result);
CallOut(index, &resultBVP, argsBVPs.data(), sizeof...(t_Args));
CallOut(key, nullptr, argsBVPs.data(), sizeof...(t_Args));
#if !defined(RELEASE)
if (!resultBVP.GetAsUnsafe<t_Return>())
{
AZ_Error("ScriptCanvas", false, "%s:CallOut(%zu) failed to provide a useable result", TYPEINFO_Name(), index);
return;
}
#endif
result = *resultBVP.GetAsUnsafe<t_Return>();
}
void ExecutionOut(const AZ::Crc32 key) const
{
// this is correct, it is up to the FunctorOut referenced by key to decide what to do with these params (whether to modify or handle strings differently)
CallOut(key, nullptr, nullptr, 0);
}
private:
// keep this here, and don't even think about putting it back in the FunctorOuts by any method*, lambda capture or other.
// programmers will need this for internal node state debugging and who knows what other reasons
// * Lua execution is an exception
ExecutionStateWeakPtr m_executionState = nullptr;
Execution::FunctorOut m_noOpFunctor;
AZStd::unordered_map<AZ::Crc32, Execution::FunctorOut> m_outs;
AZStd::vector<Execution::FunctorOut> m_outs;
};
}
@@ -91,19 +91,6 @@ namespace ScriptCanvas
AZ_Error("ScriptCanvas", m_nodeable, "null Nodeable in NodeableNode::ConfigureSlots");
}
AZ::Outcome<AZStd::pair<size_t, size_t>> NodeableNode::FindMethodAndInputIndexOfSlot(const SlotId& slotID) const
{
if (auto thisSlot = GetSlot(slotID))
{
if (thisSlot->GetType() == CombinedSlotType::DataIn)
{
return m_slotExecutionMap.FindInAndInputIndexOfSlot(slotID);
}
}
return AZ::Failure();
}
AZ::Outcome<const AZ::BehaviorClass*, AZStd::string> NodeableNode::GetBehaviorContextClass() const
{
AZ::BehaviorContext* behaviorContext = NodeableNodeCpp::GetBehaviorContext();
@@ -73,8 +73,6 @@ namespace ScriptCanvas
void ConfigureSlots() override;
AZ::Outcome<AZStd::pair<size_t, size_t>> FindMethodAndInputIndexOfSlot(const SlotId& slotID) const;
AZ::Outcome<const AZ::BehaviorClass*, AZStd::string> GetBehaviorContextClass() const;
ConstSlotsOutcome GetBehaviorContextOutName(const Slot& inSlot) const;
@@ -105,11 +105,9 @@ namespace ScriptCanvas
void DataSlotConfiguration::SetType(const AZ::BehaviorParameter& typeDesc)
{
auto dataRegistry = GetDataRegistry();
Data::Type scType = !AZ::BehaviorContextHelper::IsStringParameter(typeDesc) ? Data::FromAZType(typeDesc.m_typeId) : Data::Type::String();
auto typeIter = dataRegistry->m_creatableTypes.find(scType);
if (typeIter != dataRegistry->m_creatableTypes.end())
auto dataRegistry = GetDataRegistry();
if (dataRegistry->IsUseableInSlot(scType))
{
m_datum.SetType(scType);
}
@@ -152,22 +152,6 @@ namespace ScriptCanvas
return nullptr;
}
AZ::Outcome<AZStd::pair<size_t, size_t>> Map::FindInAndInputIndexOfSlot(const SlotId& slotID) const
{
for (const auto& in : m_ins)
{
auto inputIter = find_if(in.inputs, [&slotID](const Input& input) { return input.slotId == slotID; });
if (inputIter != in.inputs.end())
{
const size_t inIndex = &in - m_ins.begin();
const size_t inputIndex = inputIter - in.inputs.begin();
return AZ::Success(AZStd::make_pair(inIndex, inputIndex));
}
}
return AZ::Failure();
}
const In* Map::FindInFromInputSlot(const SlotId& slotID) const
{
auto iter = find_if
@@ -124,8 +124,6 @@ namespace ScriptCanvas
Map(Outs&& latents);
AZ::Outcome<AZStd::pair<size_t, size_t>> FindInAndInputIndexOfSlot(const SlotId& slotID) const;
const In* FindInFromInputSlot(const SlotId& slotID) const;
SlotId FindInputSlotIdBySource(VariableId inputSourceId, Grammar::FunctionSourceId inSourceId) const;
@@ -209,13 +209,18 @@ namespace ScriptCanvas
m_latents.push_back(out);
}
void SubgraphInterface::AddOutKey(const AZStd::string& name)
bool SubgraphInterface::AddOutKey(const AZStd::string& name)
{
const AZ::Crc32 key(name);
if (AZStd::find(m_outKeys.begin(), m_outKeys.end(), key) == m_outKeys.end())
{
m_outKeys.push_back(key);
return true;
}
else
{
return false;
}
}
@@ -708,7 +713,7 @@ namespace ScriptCanvas
}
// Populates the list of out keys
void SubgraphInterface::Parse()
AZ::Outcome<void, AZStd::string> SubgraphInterface::Parse()
{
m_outKeys.clear();
@@ -716,14 +721,22 @@ namespace ScriptCanvas
{
for (const auto& out : in.outs)
{
AddOutKey(out.displayName);
if (!AddOutKey(out.displayName))
{
return AZ::Failure(AZStd::string::format("Out %s was already in the list", out.displayName.c_str()));
}
}
}
for (const auto& latent : m_latents)
{
AddOutKey(latent.displayName);
if (!AddOutKey(latent.displayName))
{
return AZ::Failure(AZStd::string::format("Out %s was already in the list", latent.displayName.c_str()));
}
}
return AZ::Success();
}
void SubgraphInterface::Reflect(AZ::ReflectContext* refectContext)
@@ -232,7 +232,7 @@ namespace ScriptCanvas
bool operator==(const SubgraphInterface& rhs) const;
// Populates the list of out keys
void Parse();
AZ::Outcome<void, AZStd::string> Parse();
bool RequiresConstructionParameters() const;
@@ -266,7 +266,7 @@ namespace ScriptCanvas
AZStd::vector<AZ::Crc32> m_outKeys;
NamespacePath m_namespacePath;
void AddOutKey(const AZStd::string& name);
bool AddOutKey(const AZStd::string& name);
const Out* FindImmediateOut(const AZStd::string& in, const AZStd::string& out) const;
const In* FindIn(const AZStd::string& inSlotId) const;
const Out* FindLatentOut(const AZStd::string& latent) const;
@@ -389,29 +389,6 @@ namespace ScriptCanvas
return AZ::Utils::IsVectorContainerType(ToAZType(type));
}
bool IsAllowedBehaviorClassVariableType(const AZ::Uuid& id)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
AZ_Assert(behaviorContext, "Unable to retrieve behavior context.");
const auto& classIterator = behaviorContext->m_typeToClassMap.find(id);
if (classIterator != behaviorContext->m_typeToClassMap.end())
{
AZ::BehaviorClass* behaviorClass = classIterator->second;
if (behaviorClass->FindAttribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden))
{
return false;
}
}
else
{
return false;
}
return true;
}
bool IsSetContainerType(const AZ::Uuid& type)
{
return AZ::Utils::IsSetContainerType(type);
@@ -197,8 +197,6 @@ namespace ScriptCanvas
bool IsVectorContainerType(const AZ::Uuid& type);
bool IsVectorContainerType(const Type& type);
bool IsAllowedBehaviorClassVariableType(const AZ::Uuid& id);
AZStd::vector<AZ::Uuid> GetContainedTypes(const AZ::Uuid& type);
AZStd::vector<Type> GetContainedTypes(const Type& type);
AZStd::pair<AZ::Uuid, AZ::Uuid> GetOutcomeTypes(const AZ::Uuid& type);
@@ -105,14 +105,24 @@ namespace ScriptCanvas
AZ_Error("Script Canvas", it.second, "Cannot register a second Trait struct with the same ScriptCanvas type(%u)", it.first->first);
}
void DataRegistry::RegisterType(const AZ::TypeId& typeId, TypeProperties typeProperties)
void DataRegistry::RegisterType(const AZ::TypeId& typeId, TypeProperties typeProperties, Createability registration)
{
Data::Type behaviorContextType = Data::FromAZType(typeId);
if (behaviorContextType.GetType() == Data::eType::BehaviorContextObject && !behaviorContextType.GetAZType().IsNull())
{
if (m_creatableTypes.find(behaviorContextType) == m_creatableTypes.end())
if (registration == Createability::SlotAndVariable)
{
m_creatableTypes[behaviorContextType] = typeProperties;
if (m_creatableTypes.find(behaviorContextType) == m_creatableTypes.end())
{
m_creatableTypes[behaviorContextType] = typeProperties;
}
}
else if (registration == Createability::SlotOnly)
{
if (m_slottableTypes.find(behaviorContextType) == m_slottableTypes.end())
{
m_slottableTypes[behaviorContextType] = typeProperties;
}
}
}
}
@@ -125,4 +135,15 @@ namespace ScriptCanvas
m_creatableTypes.erase(behaviorContextType);
}
}
}
bool DataRegistry::IsUseableInSlot(const Data::Type& scType) const
{
return m_creatableTypes.contains(scType) || m_slottableTypes.contains(scType);
}
bool DataRegistry::IsUseableInSlot(const AZ::TypeId& typeId) const
{
Data::Type scType = Data::FromAZType(typeId);
return IsUseableInSlot(scType);
}
}
@@ -34,11 +34,21 @@ namespace ScriptCanvas
AZ_TYPE_INFO(DataRegistry, "{41049FA8-EA56-401F-9720-6FE9028A1C01}");
AZ_CLASS_ALLOCATOR(DataRegistry, AZ::SystemAllocator, 0);
void RegisterType(const AZ::TypeId& typeId, TypeProperties typeProperties);
enum class Createability
{
None,
SlotAndVariable,
SlotOnly,
};
void RegisterType(const AZ::TypeId& typeId, TypeProperties typeProperties, Createability registration);
void UnregisterType(const AZ::TypeId& typeId);
bool IsUseableInSlot(const AZ::TypeId& typeId) const;
bool IsUseableInSlot(const Data::Type& type) const;
AZStd::unordered_map<Data::eType, Data::TypeErasedTraits> m_typeIdTraitMap; // Creates a mapping of the Data::eType TypeId to the trait structure
AZStd::unordered_map<Data::Type, TypeProperties> m_creatableTypes;
AZStd::unordered_map<Data::Type, TypeProperties> m_slottableTypes;
};
void InitDataRegistry();
@@ -508,11 +508,10 @@ namespace ScriptCanvas
const int argsCount = lua_gettop(lua);
AZ_Assert(argsCount >= 2, "CallExecutionOut: Error in compiled Lua file, not enough arguments");
AZ_Assert(lua_isuserdata(lua, 1), "CallExecutionOut: Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, 2), "CallExecutionOut: Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isnumber(lua, 2), "CallExecutionOut: Error in compiled lua file, 2nd argument to SetExecutionOut is not a number");
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, 1);
const char* keyStr = AZ::ScriptValue<const char*>::StackRead(lua, 2);
AZ_Assert(keyStr, "CallExecutionOut: Failed to read key string");
nodeable->CallOut(AZ::Crc32(keyStr), nullptr, nullptr, argsCount - 2);
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
nodeable->CallOut(index, nullptr, nullptr, argsCount - 2);
// Lua: results...
return lua_gettop(lua);
}
@@ -559,23 +558,14 @@ namespace ScriptCanvas
int InitializeNodeableOutKeys(lua_State* lua)
{
using namespace ExecutionInterpretedAPICpp;
// Lua: usernodeable, outKeys...
// Lua: usernodeable, keyCount
const int argsCount = lua_gettop(lua);
AZ_Assert(argsCount >= 2, "InitializeNodeableOutKeys: Error in compiled Lua file, not enough arguments");
AZ_Assert((argsCount - 1) < k_MaxNodeableOuts, "InitializeNodeableOutKeys: Error in compiled Lua file, too many outs for nodeable out)");
AZ_Assert(argsCount == 2, "InitializeNodeableOutKeys: Error in compiled Lua file, not enough arguments");
AZ_Assert(lua_isuserdata(lua, 1), "InitializeNodeableOutKeys: Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, 1);
const int keyCount = argsCount - 1;
AZStd::array<AZ::Crc32, k_MaxNodeableOuts> keys;
for (int argumentIndex = 2, sentinel = argsCount + 1; argumentIndex != sentinel; ++argumentIndex)
{
AZ_Assert(lua_isnumber(lua, argumentIndex), "InitializeNodeableOutKeys: Error in compiled lua file, argument at Lua index #%d was not an integer", argumentIndex);
keys[argumentIndex - 2] = static_cast<AZ::u32>(lua_tointeger(lua, argumentIndex));
}
nodeable->InitializeExecutionOuts(keys.begin(), keys.begin() + keyCount);
AZ_Assert(lua_isnumber(lua, 2), "InitializeNodeableOutKeys: Error in compiled lua file, 2nd argument was not an integer");
const size_t keyCount = aznumeric_caster(lua_tointeger(lua, 2));
nodeable->InitializeExecutionOuts(keyCount);
return 0;
}
@@ -595,19 +585,18 @@ namespace ScriptCanvas
// \see https://jira.agscollab.com/browse/LY-99750
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a number");
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3);
AZ_Assert(nodeable, "Failed to read nodeable");
const char* keyStr = AZ::ScriptValue<const char*>::StackRead(lua, -2);
AZ_Assert(keyStr, "Failed to read key string");
// Lua: nodeable, string, lambda
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
// Lua: nodeable, index, lambda
lua_pushvalue(lua, -1);
// Lua: nodeable, string, lambda, lambda
// Lua: nodeable, index, lambda, lambda
nodeable->SetExecutionOut(AZ::Crc32(keyStr), OutInterpreted(lua));
// Lua: nodeable, string, lambda
nodeable->SetExecutionOut(index, OutInterpreted(lua));
// Lua: nodeable, index, lambda
// \todo clear these immediately after they are not needed with an explicit call written by the translator
return 0;
@@ -618,20 +607,19 @@ namespace ScriptCanvas
// \note Return values could become necessary.
// \see https://jira.agscollab.com/browse/LY-99750
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOutResult is not userdata (Nodeable)");
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOutResult is not a number");
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOutResult is not a function (lambda need to get around atypically routed arguments)");
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3); // this won't be a BCO, because BCOs won't be necessary in the interpreted mode...most likely
AZ_Assert(nodeable, "Failed to read nodeable");
const char* keyStr = AZ::ScriptValue<const char*>::StackRead(lua, -2);
AZ_Assert(keyStr, "Failed to read key string");
// Lua: nodeable, string, lambda
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
// Lua: nodeable, index, lambda
lua_pushvalue(lua, -1);
// Lua: nodeable, string, lambda, lambda
// Lua: nodeable, index, lambda, lambda
nodeable->SetExecutionOut(AZ::Crc32(keyStr), OutInterpretedResult(lua));
// Lua: nodeable, string, lambda
nodeable->SetExecutionOut(index, OutInterpretedResult(lua));
// Lua: nodeable, index, lambda
// \todo clear these immediately after they are not needed with an explicit call written by the translator
return 0;
@@ -639,20 +627,19 @@ namespace ScriptCanvas
int SetExecutionOutUserSubgraph(lua_State* lua)
{
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOutUserSubgraph is not userdata (Nodeable)");
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOutUserSubgraph is not a number");
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOutUserSubgraph is not a function (lambda need to get around atypically routed arguments)");
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3);
AZ_Assert(nodeable, "Failed to read nodeable");
const char* keyStr = AZ::ScriptValue<const char*>::StackRead(lua, -2);
AZ_Assert(keyStr, "Failed to read key string");
// Lua: nodeable, string, lambda
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
// Lua: nodeable, index, lambda
lua_pushvalue(lua, -1);
// Lua: nodeable, string, lambda, lambda
// Lua: nodeable, index, lambda, lambda
nodeable->SetExecutionOut(AZ::Crc32(keyStr), OutInterpretedUserSubgraph(lua));
// Lua: nodeable, string, lambda
nodeable->SetExecutionOut(index, OutInterpretedUserSubgraph(lua));
// Lua: nodeable, index, lambda
// \todo clear these immediately after they are not needed with an explicit call written by the translator
return 0;
@@ -83,7 +83,7 @@ namespace ScriptCanvas
int EBusHandlerCreateAndConnectTo(lua_State* lua)
{
// Lua: executionState, (event name) string, (address aztypeid) string, (address) ?
// Lua: executionState, (ebus name) string, (address aztypeid) string, (address) ?
auto executionState = AZ::ScriptValue<ExecutionStateInterpreted*>::StackRead(lua, 1);
auto ebusName = AZ::ScriptValue<const char*>::StackRead(lua, 2);
EBusHandler* ebusHandler = aznew EBusHandler(executionState->WeakFromThis(), ebusName, AZ::ScriptContext::FromNativeContext(lua)->GetBoundContext());
@@ -96,7 +96,7 @@ namespace ScriptCanvas
ebusHandler->ConnectTo(address);
AZ::Internal::LuaClassToStack(lua, ebusHandler, azrtti_typeid<EBusHandler>(), AZ::ObjectToLua::ByReference, AZ::AcquisitionOnPush::ScriptAcquire);
// Lua: executionState, (event name) string, (address aztypeid) string, (address) ?, handler
// Lua: executionState, (ebus name) string, (address aztypeid) string, (address) ?, handler
return 1;
}
@@ -114,27 +114,23 @@ namespace ScriptCanvas
const int k_eventNameIndex = -2;
const int k_lambdaIndex = -1;
AZ_Assert(lua_isuserdata(lua, k_nodeableIndex), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, k_eventNameIndex), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isfunction(lua, k_lambdaIndex), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
AZ_Assert(lua_isuserdata(lua, k_nodeableIndex), "Error in compiled lua file, 1st argument to EBusHandlerHandleEvent is not userdata (EBusHandler)");
AZ_Assert(lua_isnumber(lua, k_eventNameIndex), "Error in compiled lua file, 2nd argument to EBusHandlerHandleEvent is not a number");
AZ_Assert(lua_isfunction(lua, k_lambdaIndex), "Error in compiled lua file, 3rd argument to EBusHandlerHandleEvent is not a function");
auto nodeable = AZ::ScriptValue<EBusHandler*>::StackRead(lua, k_nodeableIndex); // this won't be a BCO, because BCOs won't be necessary in the interpreted mode...most likely
auto nodeable = AZ::ScriptValue<EBusHandler*>::StackRead(lua, k_nodeableIndex);
AZ_Assert(nodeable, "Failed to read EBusHandler");
const char* keyStr = lua_tostring(lua, k_eventNameIndex);
AZ_Assert(keyStr, "Failed to read key string");
const int eventIndex = nodeable->GetEventIndex(keyStr);
AZ_Assert(eventIndex != -1, "Event index was not found for %s-%s", nodeable->GetEBusName().data(), keyStr);
const int eventIndex = lua_tointeger(lua, k_eventNameIndex);
AZ_Assert(eventIndex != -1, "Event index was not found for %s", nodeable->GetEBusName().data());
// install the generic hook for the event
nodeable->HandleEvent(eventIndex);
// Lua: nodeable, string, lambda
lua_pushvalue(lua, k_lambdaIndex);
// Lua: nodeable, string, lambda, lambda
// route the event handling to the lambda on the top of the stack
nodeable->SetExecutionOut(AZ::Crc32(eventIndex), OutInterpreted(lua));
// Lua: nodeable, string, lambda
return 0;
}
@@ -145,16 +141,14 @@ namespace ScriptCanvas
const int k_eventNameIndex = -2;
const int k_lambdaIndex = -1;
AZ_Assert(lua_isuserdata(lua, k_nodeableIndex), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
AZ_Assert(lua_isstring(lua, k_eventNameIndex), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a string (Crc key)");
AZ_Assert(lua_isfunction(lua, k_lambdaIndex), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
AZ_Assert(lua_isuserdata(lua, k_nodeableIndex), "Error in compiled lua file, 1st argument to EBusHandlerHandleEventResult is not userdata (EBusHandler)");
AZ_Assert(lua_isnumber(lua, k_eventNameIndex), "Error in compiled lua file, 2nd argument to EBusHandlerHandleEventResult is not a number");
AZ_Assert(lua_isfunction(lua, k_lambdaIndex), "Error in compiled lua file, 3rd argument to EBusHandlerHandleEventResult is not a function");
auto nodeable = AZ::ScriptValue<EBusHandler*>::StackRead(lua, k_nodeableIndex); // this won't be a BCO, because BCOs won't be necessary in the interpreted mode...most likely
auto nodeable = AZ::ScriptValue<EBusHandler*>::StackRead(lua, k_nodeableIndex);
AZ_Assert(nodeable, "Failed to read EBusHandler");
const char* keyStr = lua_tostring(lua, k_eventNameIndex);
AZ_Assert(keyStr, "Failed to read key string");
const int eventIndex = nodeable->GetEventIndex(keyStr);
AZ_Assert(eventIndex != -1, "Event index was not found for %s-%s", nodeable->GetEBusName().data(), keyStr);
const int eventIndex = lua_tointeger(lua, k_eventNameIndex);
AZ_Assert(eventIndex != -1, "Event index was not found for %s", nodeable->GetEBusName().data());
// install the generic hook for the event
nodeable->HandleEvent(eventIndex);
// Lua: nodeable, string, lambda
@@ -165,7 +159,6 @@ namespace ScriptCanvas
// route the event handling to the lambda on the top of the stack
nodeable->SetExecutionOut(AZ::Crc32(eventIndex), OutInterpretedResult(lua));
// Lua: nodeable, string, lambda
return 0;
}
@@ -70,7 +70,7 @@ namespace ScriptCanvas
void RuntimeComponent::Execute()
{
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::ScriptCanvas, "RuntimeComponent::InitializeExecution (%s)", m_runtimeAsset.GetId().ToString<AZStd::string>().c_str());
AZ_PROFILE_SCOPE_DYNAMIC(AZ::Debug::ProfileCategory::ScriptCanvas, "RuntimeComponent::Execute (%s)", m_runtimeAsset.GetId().ToString<AZStd::string>().c_str());
AZ_Assert(m_executionState, "RuntimeComponent::Execute called without an execution state");
SC_EXECUTION_TRACE_GRAPH_ACTIVATED(CreateActivationInfo());
SCRIPT_CANVAS_PERFORMANCE_SCOPE_EXECUTION(m_executionState->GetScriptCanvasId(), m_runtimeAsset.GetId());
@@ -434,7 +434,7 @@ namespace ScriptCanvas
if (!root->HasExplicitUserOutCalls())
{
// there is a single out call, default or not
// there is a single out, default or not
Out out;
if (outCalls.empty())
@@ -468,9 +468,7 @@ namespace ScriptCanvas
}
else
{
// for now, all outs must return all the same output,
// if the UI changes, we'll need to track the output of each individual output
if (outCalls.empty())
if (outCalls.size() < 2)
{
AddError(root->GetNodeId(), root, ScriptCanvas::ParseErrors::NotEnoughBranchesForReturn);
return;
@@ -503,6 +501,9 @@ namespace ScriptCanvas
, returnValueVariable->m_sourceVariableId });
}
AZStd::const_pointer_cast<ExecutionTree>(outCall)->SetOutCallIndex(m_outIndexCount);
++m_outIndexCount;
in.outs.push_back(AZStd::move(out));
}
}
@@ -543,6 +544,8 @@ namespace ScriptCanvas
, returnValueVariable->m_sourceVariableId });
}
AZStd::const_pointer_cast<ExecutionTree>(outCall)->SetOutCallIndex(m_outIndexCount);
++m_outIndexCount;
m_subgraphInterface.AddLatent(AZStd::move(out));
}
@@ -908,6 +911,7 @@ namespace ScriptCanvas
ebusHandling->m_startingAdress = startingAddress;
}
ebusHandling->m_node = &node;
m_ebusHandlingByNode.emplace(&node, ebusHandling);
return true;
}
@@ -3252,6 +3256,15 @@ namespace ScriptCanvas
AZ_Assert(childOutSlot, "null slot in child out slot list");
ExecutionTreePtr internalOut = OpenScope(child, node, childOutSlot);
internalOut->SetNodeable(execution->GetNodeable());
const size_t outIndex = node->GetOutIndex(*childOutSlot);
if (outIndex == std::numeric_limits<size_t>::max())
{
AddError(execution, aznew Internal::ParseError(node->GetEntityId(), AZStd::string::format("Missing internal out key for slot %s", childOutSlot->GetName().c_str())));
return;
}
internalOut->SetOutCallIndex(outIndex);
internalOut->MarkInternalOut();
internalOut->SetSymbol(Symbol::FunctionDefinition);
auto outNameOutcome = node->GetInternalOutKey(*childOutSlot);
@@ -3893,6 +3906,14 @@ namespace ScriptCanvas
auto latentOutKeyOutcome = node.GetLatentOutKey(*slot);
if (latentOutKeyOutcome.IsSuccess())
{
const size_t outIndex = node.GetOutIndex(*slot);
if (outIndex == std::numeric_limits<size_t>::max())
{
AddError(outRoot, aznew Internal::ParseError(node.GetEntityId(), AZStd::string::format("Missing internal out key for slot %s", slot->GetName().c_str())));
return;
}
outRoot->SetOutCallIndex(outIndex);
outRoot->SetName(latentOutKeyOutcome.GetValue().data());
AZStd::const_pointer_cast<NodeableParse>(nodeableParseIter->second)->m_latents.emplace_back(outRoot->GetName(), outRoot);
}
@@ -4622,7 +4643,13 @@ namespace ScriptCanvas
m_userInsThatRequireTopology.clear();
ParseUserOuts();
m_subgraphInterface.Parse();
auto parseOutcome = m_subgraphInterface.Parse();
if (!parseOutcome.IsSuccess())
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId(), AZStd::string::format("Subgraph interface failed to parse: %s", parseOutcome.GetError().c_str()).c_str()));
}
}
void AbstractCodeModel::ParseUserIn(ExecutionTreePtr root, const Nodes::Core::FunctionDefinitionNode* nodeling)
@@ -507,6 +507,7 @@ namespace ScriptCanvas
static UserInParseTopologyResult ParseUserInTolopology(size_t nodelingsOutCount, size_t leavesWithoutNodelingsCount);
size_t m_outIndexCount = 0;
ExecutionTreePtr m_start;
AZStd::vector<const Nodes::Core::Start*> m_startNodes;
ScopePtr m_graphScope;
@@ -1057,6 +1057,13 @@ namespace ScriptCanvas
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::FunctionCallNode*>(execution->GetId().m_node);
}
bool IsUserFunctionCallPure(const ExecutionTreeConstPtr& execution)
{
return (execution->GetSymbol() == Symbol::FunctionCall)
&& azrtti_istypeof<const ScriptCanvas::Nodes::Core::FunctionCallNode*>(execution->GetId().m_node)
&& azrtti_cast<const ScriptCanvas::Nodes::Core::FunctionCallNode*>(execution->GetId().m_node)->IsPure();
}
bool IsUserFunctionDefinition(const ExecutionTreeConstPtr& execution)
{
auto nodeling = azrtti_cast<const ScriptCanvas::Nodes::Core::FunctionDefinitionNode*>(execution->GetId().m_node);
@@ -163,6 +163,8 @@ namespace ScriptCanvas
bool IsUserFunctionCall(const ExecutionTreeConstPtr& execution);
bool IsUserFunctionCallPure(const ExecutionTreeConstPtr& execution);
bool IsUserFunctionDefinition(const ExecutionTreeConstPtr& execution);
const ScriptCanvas::Nodes::Core::FunctionDefinitionNode* IsUserOutNode(const Node* node);
@@ -90,11 +90,11 @@ namespace ScriptCanvas
AZ_CLASS_ALLOCATOR(EBusHandling, AZ::SystemAllocator, 0);
bool m_isAddressed = false;
const Node* m_node = nullptr;
VariableConstPtr m_startingAdress;
AZStd::string m_ebusName;
AZStd::string m_handlerName;
AZStd::vector<AZStd::pair<AZStd::string, ExecutionTreeConstPtr>> m_events;
void Clear();
};
@@ -110,7 +110,7 @@ namespace ScriptCanvas
constexpr const char* k_InitializeStaticsName = "InitializeStatics";
constexpr const char* k_InitializeNodeableOutKeys = "InitializeNodeableOutKeys";
constexpr const char* k_InitializeExecutionOutByRequiredCountName = "InitializeExecutionOutByRequiredCount";
constexpr const char* k_InterpretedConfigurationPerformance = "SCRIPT_CANVAS_GLOBAL_PERFORMANCE";
constexpr const char* k_InterpretedConfigurationRelease = "SCRIPT_CANVAS_GLOBAL_RELEASE";
@@ -260,6 +260,11 @@ namespace ScriptCanvas
return m_nodeable;
}
AZStd::optional<size_t> ExecutionTree::GetOutCallIndex() const
{
return m_outCallIndex != std::numeric_limits<size_t>::max() ? AZStd::optional<size_t>(m_outCallIndex) : AZStd::nullopt;
}
ExecutionTreeConstPtr ExecutionTree::GetParent() const
{
return m_parent;
@@ -559,6 +564,11 @@ namespace ScriptCanvas
m_lexicalScope = lexicalScope;
}
void ExecutionTree::SetOutCallIndex(size_t index)
{
m_outCallIndex = index;
}
void ExecutionTree::SetParent(ExecutionTreePtr parent)
{
m_parent = parent;
@@ -170,6 +170,8 @@ namespace ScriptCanvas
VariableConstPtr GetNodeable() const;
AZStd::optional<size_t> GetOutCallIndex() const;
ExecutionTreeConstPtr GetParent() const;
ExecutionTreeConstPtr GetRoot() const;
@@ -252,6 +254,8 @@ namespace ScriptCanvas
void SetNodeable(VariableConstPtr nodeable);
void SetOutCallIndex(size_t index);
void SetParent(ExecutionTreePtr parent);
void SetScope(ScopePtr scope);
@@ -286,6 +290,8 @@ namespace ScriptCanvas
bool m_hasExplicitUserOutCalls = false;
size_t m_outCallIndex = std::numeric_limits<size_t>::max();
// The node and the activation slot. The execution in, or the event or latent out slot.
ExecutionId m_in;
@@ -66,7 +66,7 @@ namespace ScriptCanvas
break;
}
while (m_timerCounter > m_timerDuration)
while (m_timerCounter >= m_timerDuration)
{
if (!m_isActive)
{
@@ -393,6 +393,11 @@ namespace ScriptCanvas
return false;
}
AZStd::optional<size_t> EBusEventHandler::GetEventIndex(AZStd::string eventName) const
{
return m_handler->GetFunctionIndex(eventName.c_str());
}
const EBusEventEntry* EBusEventHandler::FindEvent(const AZStd::string& name) const
{
AZ::Crc32 key = AZ::Crc32(name.c_str());
@@ -115,6 +115,7 @@ namespace ScriptCanvas
AZ::Outcome<AZStd::string, void> GetFunctionCallName(const Slot* /*slot*/) const override;
bool IsEBusAddressed() const override;
AZStd::optional<size_t> GetEventIndex(AZStd::string eventName) const;
const EBusEventEntry* FindEvent(const AZStd::string& name) const;
AZStd::string GetEBusName() const override;
bool IsAutoConnected() const override;
@@ -548,6 +548,11 @@ namespace ScriptCanvas
return EBusEventHandlerProperty::GetDisconnectSlot(this);
}
AZStd::optional<size_t> ReceiveScriptEvent::GetEventIndex(AZStd::string eventName) const
{
return m_handler->GetFunctionIndex(eventName.c_str());;
}
AZStd::vector<SlotId> ReceiveScriptEvent::GetEventSlotIds() const
{
AZStd::vector<SlotId> eventSlotIds;
@@ -57,6 +57,7 @@ namespace ScriptCanvas
AZ::Outcome<AZStd::string> GetInternalOutKey(const Slot& slot) const override;
const Slot* GetEBusConnectSlot() const override;
const Slot* GetEBusDisconnectSlot() const override;
AZStd::optional<size_t> GetEventIndex(AZStd::string eventName) const override;
AZStd::vector<SlotId> GetEventSlotIds() const override;
AZStd::vector<SlotId> GetNonEventSlotIds() const override;
@@ -136,16 +136,21 @@ namespace ScriptCanvas
}
protected:
size_t GetRequiredOutCount() const override
{
return 2;
}
void Lerp(float t)
{
const t_Operand step = m_start + (m_difference * t);
// make a release note that the lerp complete and tick slot are two different execution threads
ExecutionOut(AZ_CRC_CE("Tick"), step, t);
ExecutionOut(0, step, t);
if (AZ::IsClose(t, 1.0f, AZ::Constants::FloatEpsilon))
{
StopLerp();
ExecutionOut(AZ_CRC_CE("Lerp Complete"));
ExecutionOut(1);
}
}
@@ -22,6 +22,7 @@
#include <ScriptCanvas/Core/ScriptCanvasBus.h>
#include <ScriptCanvas/Variable/VariableCore.h>
#include <ScriptCanvas/PerformanceTracker.h>
#include <ScriptCanvas/Data/DataRegistry.h>
namespace AZ
{
@@ -65,6 +66,8 @@ namespace ScriptCanvas
inline bool IsAnyScriptInterpreted() const { return true; }
AZStd::pair<DataRegistry::Createability, TypeProperties> GetCreatibility(AZ::SerializeContext* serializeContext, AZ::BehaviorClass* behaviorClass);
// SystemRequestBus::Handler...
bool IsScriptUnitTestingInProgress() override;
void MarkScriptUnitTestBegin() override;
@@ -100,7 +103,7 @@ namespace ScriptCanvas
using LockType = AZStd::lock_guard<MutexType>;
AZStd::unordered_map<const void*, BehaviorContextObject*> m_ownedObjectsByAddress;
MutexType m_ownedObjectsByAddressMutex;
int m_infiniteLoopDetectionMaxIterations = 3000;
int m_infiniteLoopDetectionMaxIterations = 1000000;
int m_maxHandlerStackDepth = 50;
static void SafeRegisterPerformanceTracker();
@@ -689,8 +689,16 @@ namespace ScriptCanvas
void GraphToLua::TranslateExecutionTreeUserOutCall(Grammar::ExecutionTreeConstPtr execution)
{
// \todo revisit with per-entity run time storage that keeps execution out calls
m_dotLua.WriteIndented("%s(self, \"%s\"", Grammar::k_NodeableCallInterpretedOut, execution->GetName().data());
auto outCallIndexOptional = execution->GetOutCallIndex();
if (!outCallIndexOptional)
{
AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), "Execution did not return required out call index"));
return;
}
const size_t outIndex = *outCallIndexOptional;
m_dotLua.WriteIndented("%s(self, %zu", Grammar::k_NodeableCallInterpretedOut, outIndex);
if (execution->GetInputCount() > 0)
{
@@ -698,7 +706,7 @@ namespace ScriptCanvas
WriteFunctionCallInput(execution);
}
m_dotLua.WriteLine(")");
m_dotLua.WriteLine(") -- %s", execution->GetName().data());
}
void GraphToLua::TranslateFunction(Grammar::ExecutionTreeConstPtr execution, IsNamed lex)
@@ -861,14 +869,23 @@ namespace ScriptCanvas
const bool hasResults = eventThread->HasReturnValues();
AZStd::optional<size_t> eventIndex = ebusHandling->m_node->GetEventIndex(nameAndEventThread.first);
if (!eventIndex)
{
AddError(nullptr, aznew Internal::ParseError(ebusHandling->m_node->GetEntityId(), AZStd::string::format("EBus handler did not return a valid index for event %s", nameAndEventThread.first.c_str())));
return;
}
m_dotLua.WriteNewLine();
m_dotLua.WriteLineIndented("%s(%s%s, '%s',"
m_dotLua.WriteLineIndented("%s(%s%s, %zu, -- %s"
, hasResults ? Grammar::k_EBusHandlerHandleEventResultName : Grammar::k_EBusHandlerHandleEventName
, leftValue.data()
, ebusHandling->m_handlerName.data()
, *eventIndex
, eventThread->GetName().data());
m_dotLua.Indent();
TranslateFunction(eventThread, IsNamed::No);
m_dotLua.WriteLine(")");
@@ -983,14 +1000,7 @@ namespace ScriptCanvas
const auto& outKeys = m_model.GetInterface().GetOutKeys();
if (!outKeys.empty())
{
m_dotLua.WriteIndented("%s(self", Grammar::k_InitializeNodeableOutKeys);
for (auto& key : outKeys)
{
m_dotLua.Write(", %u", AZ::u32(key));
}
m_dotLua.WriteLine(")");
m_dotLua.WriteLineIndented("%s(self, %zu)", Grammar::k_InitializeNodeableOutKeys, outKeys.size());
}
}
else
@@ -1009,15 +1019,26 @@ namespace ScriptCanvas
void GraphToLua::TranslateNodeableOut(Grammar::ExecutionTreeConstPtr execution)
{
auto outCallIndexOptional = execution->GetOutCallIndex();
if (!outCallIndexOptional)
{
AddError(nullptr, aznew Internal::ParseError(execution->GetNodeId(), "Execution did not return required out call index"));
return;
}
const size_t outIndex = *outCallIndexOptional;
// #functions2 remove-execution-out-hash
const auto setExecutionOutName = Grammar::IsUserFunctionDefinition(execution)
? Grammar::k_NodeableSetExecutionOutUserSubgraphName
: execution->HasReturnValues()
? Grammar::k_NodeableSetExecutionOutResultName
: Grammar::k_NodeableSetExecutionOutName;
m_dotLua.WriteLineIndented("%s(self.%s, '%s',"
m_dotLua.WriteLineIndented("%s(self.%s, %zu, -- %s"
, setExecutionOutName
, execution->GetNodeable()->m_name.data()
, execution->GetNodeable()->m_name.data()
, outIndex
, execution->GetName().data());
m_dotLua.Indent();
@@ -1050,7 +1071,6 @@ namespace ScriptCanvas
return;
}
m_dotLua.WriteIndented("function %s.%s(self, ", m_tableName.c_str(), Grammar::k_InitializeStaticsName);
WriteStaticInitializerInput(IsLeadingCommaRequired::No);
m_dotLua.WriteLine(")");
@@ -1078,7 +1098,7 @@ namespace ScriptCanvas
continue;
}
if (variable->m_datum.GetType().GetAZType() == azrtti_typeid<Nodeable>())
if (m_model.IsUserNodeable(variable))
{
auto nodeableName = variable->m_name;
if (nodeableName.starts_with(Grammar::k_memberNamePrefix))
@@ -1104,7 +1124,7 @@ namespace ScriptCanvas
// indexInfo->second.requiresCtorParamsForDependencies
// self.nonLeafDependency = NonLeafDependency.new(executionState, UnpackDependencyArgs(executionState, dependentAssets, 7))
// -- has more dependencies, index, known from compile time, pushes the correct asset further down construction
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %s))"
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %zu))"
, leftValue.data()
, variable->m_name.data()
, nodeableName.data()
@@ -1112,7 +1132,7 @@ namespace ScriptCanvas
, Grammar::k_UnpackDependencyConstructionArgsFunctionName
, Grammar::k_executionStateVariableName
, Grammar::k_DependentAssetsArgName
, AZStd::to_string(indexInfo->first).data());
, indexInfo->first);
}
else // vs.
@@ -1120,7 +1140,7 @@ namespace ScriptCanvas
// !indexInfo->second.hasMoreDependencies
// self.leafDependency = LeafDependency.new(executionState, UnpackDependencyArgsLeaf(executionState, dependentAssets, 10))
// -- has NO more dependencies, index, known from compile time
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %s))"
m_dotLua.WriteLineIndented("%s%s = %s.new(%s, %s(%s, %s, %zu))"
, leftValue.data()
, variable->m_name.data()
, nodeableName.data()
@@ -1128,7 +1148,7 @@ namespace ScriptCanvas
, Grammar::k_UnpackDependencyConstructionArgsLeafFunctionName
, Grammar::k_executionStateVariableName
, Grammar::k_DependentAssetsArgName
, AZStd::to_string(indexInfo->first).data());
, indexInfo->first);
}
}
else
@@ -1158,6 +1178,7 @@ namespace ScriptCanvas
case Grammar::VariableConstructionRequirement::InputNodeable:
m_dotLua.WriteLineIndented("%s:InitializeExecutionState(%s)", variable->m_name.data(), Grammar::k_executionStateVariableName);
m_dotLua.WriteLineIndented("%s:%s()", variable->m_name.c_str(), Grammar::k_InitializeExecutionOutByRequiredCountName);
m_dotLua.WriteLineIndented("%s%s = %s", leftValue.data(), variable->m_name.data(), variable->m_name.data());
break;
@@ -1227,9 +1248,7 @@ namespace ScriptCanvas
void GraphToLua::WriteConstructionDependencyArgs()
{
auto& dependencyArgs = m_model.GetOrderedDependencies().orderedAssetIds;
if (!dependencyArgs.empty())
if (m_model.GetInterface().RequiresConstructionParametersForDependencies())
{
m_dotLua.Write(", %s", Grammar::k_DependentAssetsArgName);
}
@@ -1740,7 +1759,7 @@ namespace ScriptCanvas
size_t GraphToLua::WriteFunctionCallInputThisPointer(Grammar::ExecutionTreeConstPtr execution)
{
if (IsUserFunctionCall(execution) && execution->GetRoot()->IsPure())
if (IsUserFunctionCallPure(execution))
{
m_dotLua.Write("%s", Grammar::k_executionStateVariableName);
@@ -81,6 +81,8 @@ namespace ScriptCanvas
{
return ConstructCustomNodeIdentifier(scriptCanvasNode->RTTI_GetType());
}
return NodeTypeIdentifier(0);
}
NodeTypeIdentifier NodeUtils::ConstructEBusIdentifier(ScriptCanvas::EBusBusId ebusIdentifier)
@@ -43,20 +43,12 @@ namespace ScriptCanvas
{
const char* GetScopeDisplayLabel(Scope scopeType)
{
switch (scopeType)
{
case Scope::Graph:
return "Graph";
case Scope::Function:
return "Function";
default:
return "?";
}
return GraphVariable::s_ScopeNames[static_cast<int>(scopeType)];
}
Scope GetScopeFromLabel(const char* label)
{
if (strcmp("Function", label) == 0)
if (strcmp(GraphVariable::s_ScopeNames[static_cast<int>(VariableFlags::Scope::Function)], label) == 0)
{
return Scope::Function;
}
@@ -71,6 +63,7 @@ namespace ScriptCanvas
case Scope::Graph:
return "Variable is accessible in the entire graph.";
case Scope::Function:
case Scope::FunctionReadOnly:
return "Variable is accessible only in the execution path of the function that defined it";
default:
return "?";
@@ -162,6 +155,14 @@ namespace ScriptCanvas
"From Component"
};
const char* GraphVariable::s_ScopeNames[static_cast<int>(VariableFlags::Scope::COUNT)] =
{
"Graph",
"Function",
"Function",
};
void GraphVariable::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
@@ -197,6 +198,13 @@ namespace ScriptCanvas
return choices;
};
auto scopeChoices = [] {
AZStd::vector< AZStd::pair<VariableFlags::Scope, AZStd::string>> choices;
choices.emplace_back(AZStd::make_pair(VariableFlags::Scope::Graph, s_ScopeNames[0]));
choices.emplace_back(AZStd::make_pair(VariableFlags::Scope::Function, s_ScopeNames[1]));
return choices;
};
editContext->Class<GraphVariable>("Variable", "Represents a Variable field within a Script Canvas Graph")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetVisibility)
@@ -215,8 +223,8 @@ namespace ScriptCanvas
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GraphVariable::OnValueChanged)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &GraphVariable::m_scope, "Scope", "Controls the scope of this variable. i.e. If this is exposed as input to this script, or output from this script, or if the variable is just locally scoped.")
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetInputControlVisibility)
->Attribute(AZ::Edit::Attributes::GenericValueList, &GraphVariable::GetScopes)
->Attribute(AZ::Edit::Attributes::Visibility, &GraphVariable::GetScopeControlVisibility)
->Attribute(AZ::Edit::Attributes::GenericValueList, scopeChoices)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &GraphVariable::OnScopeTypedChanged)
->DataElement(AZ::Edit::UIHandlers::Default, &GraphVariable::m_networkProperties, "Network Properties", "Enables whether or not this value should be network synchronized")
@@ -382,6 +390,16 @@ namespace ScriptCanvas
m_inputControlVisibility = inputControlVisibility;
}
AZ::Crc32 GraphVariable::GetScopeControlVisibility() const
{
if (m_scope == VariableFlags::Scope::FunctionReadOnly)
{
return AZ::Edit::PropertyVisibility::Hide;
}
return GetInputControlVisibility();
}
AZ::Crc32 GraphVariable::GetInputControlVisibility() const
{
return m_inputControlVisibility;
@@ -462,6 +480,8 @@ namespace ScriptCanvas
return m_scope == VariableFlags::Scope::Graph;
// All graph variables are in function local scope
case VariableFlags::Scope::Function:
case VariableFlags::Scope::FunctionReadOnly:
return true;
}
@@ -52,8 +52,10 @@ namespace ScriptCanvas
enum class Scope : AZ::u8
{
Graph = 0,
Function = 1,
Graph,
Function,
FunctionReadOnly,
COUNT
};
enum InitialValueSource : AZ::u8
@@ -142,6 +144,7 @@ namespace ScriptCanvas
void SetScriptInputControlVisibility(const AZ::Crc32& inputControlVisibility);
AZ::Crc32 GetInputControlVisibility() const;
AZ::Crc32 GetScopeControlVisibility() const;
AZ::Crc32 GetScriptInputControlVisibility() const;
AZ::Crc32 GetNetworkSettingsVisibility() const;
AZ::Crc32 GetFunctionInputControlVisibility() const;
@@ -181,6 +184,7 @@ namespace ScriptCanvas
int GetSortPriority() const;
static const char* s_InitialValueSourceNames[VariableFlags::InitialValueSource::COUNT];
static const char* s_ScopeNames[static_cast<int>(VariableFlags::Scope::COUNT)];
private:
@@ -225,7 +225,7 @@ namespace ScriptCanvas
}
// #functions2 slot<->variable add this to the graph, using the old datum
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariable(AZStd::string_view name, const Datum& value)
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariable(AZStd::string_view name, const Datum& value, bool functionScope)
{
if (FindVariable(name))
{
@@ -245,6 +245,10 @@ namespace ScriptCanvas
GraphVariable* variable = m_variableData.FindVariable(newId);
variable->SetOwningScriptCanvasId(GetScriptCanvasId());
if (functionScope)
{
variable->SetScope(VariableFlags::Scope::FunctionReadOnly);
}
VariableRequestBus::MultiHandler::BusConnect(GraphScopedVariableId(m_scriptCanvasId, newId));
GraphVariableManagerNotificationBus::Event(GetScriptCanvasId(), &GraphVariableManagerNotifications::OnVariableAddedToGraph, newId, name);
@@ -254,7 +258,7 @@ namespace ScriptCanvas
AZ::Outcome<VariableId, AZStd::string> GraphVariableManagerComponent::AddVariablePair(const AZStd::pair<AZStd::string_view, Datum>& keyValuePair)
{
return AddVariable(keyValuePair.first, keyValuePair.second);
return AddVariable(keyValuePair.first, keyValuePair.second, false);
}
VariableValidationOutcome GraphVariableManagerComponent::IsNameValid(AZStd::string_view varName)
@@ -63,7 +63,7 @@ namespace ScriptCanvas
//// GraphVariableManagerRequestBus
AZ::Outcome<VariableId, AZStd::string> CloneVariable(const GraphVariable& variableConfiguration) override;
AZ::Outcome<VariableId, AZStd::string> RemapVariable(const GraphVariable& variableConfiguration) override;
AZ::Outcome<VariableId, AZStd::string> AddVariable(AZStd::string_view name, const Datum& value) override;
AZ::Outcome<VariableId, AZStd::string> AddVariable(AZStd::string_view name, const Datum& value, bool functionScope) override;
AZ::Outcome<VariableId, AZStd::string> AddVariablePair(const AZStd::pair<AZStd::string_view, Datum>& nameValuePair) override;
VariableValidationOutcome IsNameValid(AZStd::string_view key) override;
@@ -90,7 +90,7 @@ namespace ScriptCanvas
//! returns an AZ::Outcome which on success contains the VariableId and on Failure contains a string with error information
virtual AZ::Outcome<VariableId, AZStd::string> CloneVariable(const GraphVariable& baseVariable) = 0;
virtual AZ::Outcome<VariableId, AZStd::string> RemapVariable(const GraphVariable& variableConfiguration) = 0;
virtual AZ::Outcome<VariableId, AZStd::string> AddVariable(AZStd::string_view key, const Datum& value) = 0;
virtual AZ::Outcome<VariableId, AZStd::string> AddVariable(AZStd::string_view key, const Datum& value, bool functionScope) = 0;
virtual AZ::Outcome<VariableId, AZStd::string> AddVariablePair(const AZStd::pair<AZStd::string_view, Datum>& keyValuePair) = 0;
virtual VariableValidationOutcome IsNameValid(AZStd::string_view variableName) = 0;
@@ -23,7 +23,6 @@
#include <ScriptCanvas/Core/Node.h>
#include <ScriptCanvas/Core/Nodeable.h>
#include <ScriptCanvas/Core/Slot.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Execution/ExecutionPerformanceTimer.h>
#include <ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.h>
#include <ScriptCanvas/Execution/RuntimeComponent.h>
@@ -37,10 +36,10 @@
namespace ScriptCanvasSystemComponentCpp
{
#if !defined(_RELEASE) && !defined(PERFORMANCE_BUILD)
const int k_infiniteLoopDetectionMaxIterations = 3000;
const int k_infiniteLoopDetectionMaxIterations = 1000000;
const int k_maxHandlerStackDepth = 25;
#else
const int k_infiniteLoopDetectionMaxIterations = 10000;
const int k_infiniteLoopDetectionMaxIterations = 10000000;
const int k_maxHandlerStackDepth = 100;
#endif
@@ -285,6 +284,72 @@ namespace ScriptCanvas
m_ownedObjectsByAddress.erase(object);
}
AZStd::pair<DataRegistry::Createability, TypeProperties> SystemComponent::GetCreatibility(AZ::SerializeContext* serializeContext, AZ::BehaviorClass* behaviorClass)
{
TypeProperties typeProperties;
bool canCreate{};
// BehaviorContext classes with the ExcludeFrom attribute with a value of the ExcludeFlags::List is not creatable
const AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List;
auto excludeClassAttributeData = azrtti_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
const AZ::u64 flags = excludeClassAttributeData ? excludeClassAttributeData->Get(nullptr) : 0;
bool listOnly = ((flags & AZ::Script::Attributes::ExcludeFlags::ListOnly) == AZ::Script::Attributes::ExcludeFlags::ListOnly); // ListOnly exclusions may create variables
canCreate = listOnly || (!excludeClassAttributeData || (!(flags & exclusionFlags)));
canCreate = canCreate && (serializeContext->FindClassData(behaviorClass->m_typeId));
canCreate = canCreate && !ScriptCanvasSystemComponentCpp::IsDeprecated(behaviorClass->m_attributes);
if (canCreate)
{
for (auto base : behaviorClass->m_baseClasses)
{
if (AZ::Component::TYPEINFO_Uuid() == base)
{
canCreate = false;
break; // only out of the for : base classes loop. DO NOT break out of the parent loop.
}
}
}
// Assets are not safe enough for variable creation, yet. They can be created with one Az type (Data::Asset<T>), but set to nothing.
// When read back in, they will (if lucky) just be Data::Asset<Data>, which breaks type safety at best, and requires a lot of sanity checking.
// This is NOT blacked at the createable types or BehaviorContext level, since they could be used to at least pass information through,
// and may be used other scripting contexts.
AZ::IRttiHelper* rttiHelper = behaviorClass->m_azRtti;
if (rttiHelper && rttiHelper->GetGenericTypeId() == azrtti_typeid<AZ::Data::Asset>())
{
canCreate = false;
}
if (AZ::FindAttribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, behaviorClass->m_attributes))
{
canCreate = true;
typeProperties.m_isTransient = true;
}
// create able variables must have full memory support
canCreate = canCreate &&
(behaviorClass->m_allocate
&& behaviorClass->m_cloner
&& behaviorClass->m_mover
&& behaviorClass->m_destructor
&& behaviorClass->m_deallocate) &&
AZStd::none_of(behaviorClass->m_baseClasses.begin(), behaviorClass->m_baseClasses.end(), [](const AZ::TypeId& base) { return azrtti_typeid<AZ::Component>() == base; });
if (!canCreate)
{
return { DataRegistry::Createability::None , TypeProperties{} };
}
else if (!AZ::FindAttribute(AZ::ScriptCanvasAttributes::VariableCreationForbidden, behaviorClass->m_attributes))
{
return { DataRegistry::Createability::SlotAndVariable, typeProperties };
}
else
{
return { DataRegistry::Createability::SlotOnly, typeProperties };
}
}
void SystemComponent::RegisterCreatableTypes()
{
AZ::SerializeContext* serializeContext{};
@@ -297,40 +362,11 @@ namespace ScriptCanvas
auto dataRegistry = ScriptCanvas::GetDataRegistry();
for (const auto& classIter : behaviorContext->m_classes)
{
TypeProperties typeProperties;
bool canCreate{};
const AZ::BehaviorClass* behaviorClass = classIter.second;
// BehaviorContext classes with the ExcludeFrom attribute with a value of the ExcludeFlags::List is not creatable
const AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List;
auto excludeClassAttributeData = azrtti_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
const AZ::u64 flags = excludeClassAttributeData ? excludeClassAttributeData->Get(nullptr) : 0;
bool listOnly = ((flags & AZ::Script::Attributes::ExcludeFlags::ListOnly) == AZ::Script::Attributes::ExcludeFlags::ListOnly); // ListOnly exclusions may create variables
canCreate = listOnly || (!excludeClassAttributeData || (!(flags & exclusionFlags)));
canCreate = canCreate && (serializeContext->FindClassData(behaviorClass->m_typeId));
canCreate = canCreate && !ScriptCanvasSystemComponentCpp::IsDeprecated(behaviorClass->m_attributes);
if (AZ::FindAttribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, behaviorClass->m_attributes))
{
canCreate = true;
typeProperties.m_isTransient = true;
}
// create able variables must have full memory support
canCreate = canCreate &&
( behaviorClass->m_allocate
&& behaviorClass->m_cloner
&& behaviorClass->m_mover
&& behaviorClass->m_destructor
&& behaviorClass->m_deallocate) &&
AZStd::none_of(behaviorClass->m_baseClasses.begin(), behaviorClass->m_baseClasses.end(), [](const AZ::TypeId& base) { return azrtti_typeid<AZ::Component>() == base; });
if (canCreate)
{
dataRegistry->RegisterType(behaviorClass->m_typeId, typeProperties);
}
auto createability = GetCreatibility(serializeContext, classIter.second);
if (createability.first != DataRegistry::Createability::None)
{
dataRegistry->RegisterType(classIter.second->m_typeId, createability.second, createability.first);
}
}
}
@@ -339,33 +375,19 @@ namespace ScriptCanvas
auto dataRegistry = ScriptCanvas::GetDataRegistry();
if (!dataRegistry)
{
AZ_Warning("ScriptCanvas", false, "Data registry not available. Can't register new class.");
return;
}
AZ::SerializeContext* serializeContext{};
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(serializeContext, "Serialize Context should not be missing at this point");
AZ_Assert(serializeContext, "Serialize Context missing. Can't register new class.");
TypeProperties typeProperties;
// BehaviorContext classes with the ExcludeFrom attribute with a value of the ExcludeFlags::List is not creatable
const AZ::u64 exclusionFlags = AZ::Script::Attributes::ExcludeFlags::List;
auto excludeClassAttributeData = azrtti_cast<const AZ::Edit::AttributeData<AZ::Script::Attributes::ExcludeFlags>*>(AZ::FindAttribute(AZ::Script::Attributes::ExcludeFrom, behaviorClass->m_attributes));
bool canCreate = !excludeClassAttributeData || !(excludeClassAttributeData->Get(nullptr) & exclusionFlags);
canCreate = canCreate && (serializeContext->FindClassData(behaviorClass->m_typeId) || AZ::FindAttribute(AZ::ScriptCanvasAttributes::AllowInternalCreation, behaviorClass->m_attributes));
canCreate = canCreate && !ScriptCanvasSystemComponentCpp::IsDeprecated(behaviorClass->m_attributes);
// create able variables must have full memory support
canCreate = canCreate &&
(behaviorClass->m_allocate
&& behaviorClass->m_cloner
&& behaviorClass->m_mover
&& behaviorClass->m_destructor
&& behaviorClass->m_deallocate) &&
AZStd::none_of(behaviorClass->m_baseClasses.begin(), behaviorClass->m_baseClasses.end(), [](const AZ::TypeId& base) { return azrtti_typeid<AZ::Component>() == base; });
if (canCreate)
auto createability = GetCreatibility(serializeContext, behaviorClass);
if (createability.first != DataRegistry::Createability::None)
{
dataRegistry->RegisterType(behaviorClass->m_typeId, typeProperties);
dataRegistry->RegisterType(behaviorClass->m_typeId, createability.second, createability.first);
}
}