Merge branch 'main' into default_3rdparty

This commit is contained in:
scottr
2021-06-03 16:24:55 -07:00
28 changed files with 4960 additions and 5634 deletions
@@ -127,13 +127,14 @@ namespace AZ
*/
class EditContext
{
public:
/// @cond EXCLUDE_DOCS
class ClassBuilder;
class EnumBuilder;
using ClassInfo = ClassBuilder; ///< @deprecated Use EditContext::ClassBuilder
using EnumInfo = EnumBuilder; ///< @deprecated Use EditContext::EnumBuilder
/// @endcond
public:
AZ_CLASS_ALLOCATOR(EditContext, SystemAllocator, 0);
/**
@@ -186,6 +187,7 @@ namespace AZ
* look at the unit tests and example to see use cases.
*
*/
public:
class ClassBuilder
{
friend EditContext;
@@ -399,6 +401,7 @@ namespace AZ
EnumBuilder* Value(const char* name, E value);
};
private:
typedef AZStd::list<Edit::ClassData> ClassDataListType;
typedef AZStd::unordered_map<AZ::Uuid, Edit::ElementData> EnumDataMapType;
@@ -101,6 +101,9 @@ namespace AZ
class SerializeContext
: public ReflectContext
{
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
public:
/// @cond EXCLUDE_DOCS
friend class EditContext;
class ClassBuilder;
@@ -108,9 +111,6 @@ namespace AZ
/// @endcond
class EnumBuilder;
static const unsigned int VersionClassDeprecated = (unsigned int)-1;
public:
class ClassData;
struct EnumerateInstanceCallContext;
struct ClassElement;
@@ -1131,6 +1131,7 @@ namespace AZ
* ->Version(3,&MyVersionConverter)
* ->Field("data",&MyStruct::m_data);
*/
public:
class ClassBuilder
{
friend class SerializeContext;
@@ -1330,7 +1331,8 @@ namespace AZ
AZStd::vector<AttributeSharedPair, AZStdFunctorAllocator>* m_currentAttributes = nullptr;
};
EditContext* m_editContext; ///< Pointer to optional edit context.
private:
EditContext* m_editContext; ///< Pointer to optional edit context.
UuidToClassMap m_uuidMap; ///< Map for all class in this serialize context
AZStd::unordered_multimap<AZ::Crc32, AZ::Uuid> m_classNameToUuid; /// Map all class names to their uuid
AZStd::unordered_multimap<Uuid, GenericClassInfo*> m_uuidGenericMap; ///< Uuid to ClassData map of reflected classes with GenericTypeInfo
@@ -294,7 +294,8 @@ namespace O3DE::ProjectManager
RegisterThisEngine();
return result == 0 && !PyErr_Occurred();
} catch ([[maybe_unused]] const std::exception& e)
}
catch ([[maybe_unused]] const std::exception& e)
{
AZ_Warning("ProjectManagerWindow", false, "Py_Initialize() failed with %s", e.what());
return false;
@@ -320,25 +321,25 @@ namespace O3DE::ProjectManager
bool registrationResult = true; // already registered is considered successful
bool pythonResult = ExecuteWithLock(
[&]
{
// check current engine path against all other registered engines
// to see if we are already registered
auto allEngines = m_manifest.attr("get_engines")();
if (pybind11::isinstance<pybind11::list>(allEngines))
{
// check current engine path against all other registered engines
// to see if we are already registered
auto allEngines = m_manifest.attr("get_engines")();
if (pybind11::isinstance<pybind11::list>(allEngines))
for (auto engine : allEngines)
{
for (auto engine : allEngines)
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
if (enginePath.Compare(m_enginePath) == 0)
{
AZ::IO::FixedMaxPath enginePath(Py_To_String(engine["path"]));
if (enginePath.Compare(m_enginePath) == 0)
{
return;
}
return;
}
}
}
auto result = m_register.attr("register")(m_enginePath.c_str());
registrationResult = (result.cast<int>() == 0);
});
auto result = m_register.attr("register")(m_enginePath.c_str());
registrationResult = (result.cast<int>() == 0);
});
bool finalResult = (registrationResult && pythonResult);
AZ_Assert(finalResult, "Registration of this engine failed!");
@@ -378,12 +379,12 @@ namespace O3DE::ProjectManager
auto o3deData = m_manifest.attr("load_o3de_manifest")();
if (pybind11::isinstance<pybind11::dict>(o3deData))
{
engineInfo.m_path = Py_To_String(enginePath);
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
engineInfo.m_path = Py_To_String(enginePath);
engineInfo.m_defaultGemsFolder = Py_To_String(o3deData["default_gems_folder"]);
engineInfo.m_defaultProjectsFolder = Py_To_String(o3deData["default_projects_folder"]);
engineInfo.m_defaultRestrictedFolder = Py_To_String(o3deData["default_restricted_folder"]);
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
engineInfo.m_thirdPartyPath = Py_To_String(o3deData["default_third_party_folder"]);
engineInfo.m_defaultTemplatesFolder = Py_To_String(o3deData["default_templates_folder"]);
engineInfo.m_thirdPartyPath = Py_To_String(o3deData["default_third_party_folder"]);
}
auto engineData = m_manifest.attr("get_engine_json_data")(pybind11::none(), enginePath);
@@ -391,8 +392,8 @@ namespace O3DE::ProjectManager
{
try
{
engineInfo.m_version = Py_To_String_Optional(engineData,"O3DEVersion","0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData,"engine_name","O3DE");
engineInfo.m_version = Py_To_String_Optional(engineData, "O3DEVersion", "0.0.0.0");
engineInfo.m_name = Py_To_String_Optional(engineData, "engine_name", "O3DE");
}
catch ([[maybe_unused]] const std::exception& e)
{
@@ -416,20 +417,20 @@ namespace O3DE::ProjectManager
bool PythonBindings::SetEngineInfo(const EngineInfo& engineInfo)
{
bool result = ExecuteWithLock([&] {
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str enginePath = engineInfo.m_path.toStdString();
pybind11::str defaultProjectsFolder = engineInfo.m_defaultProjectsFolder.toStdString();
pybind11::str defaultGemsFolder = engineInfo.m_defaultGemsFolder.toStdString();
pybind11::str defaultTemplatesFolder = engineInfo.m_defaultTemplatesFolder.toStdString();
pybind11::str defaultThridPartyFolder = engineInfo.m_thirdPartyPath.toStdString();
auto registrationResult = m_register.attr("register")(
enginePath, // engine_path
pybind11::none(), // project_path
enginePath, // engine_path
pybind11::none(), // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
defaultProjectsFolder,
defaultGemsFolder,
@@ -465,12 +466,12 @@ namespace O3DE::ProjectManager
QVector<GemInfo> gems;
auto result = ExecuteWithLockErrorHandling([&]
{
for (auto path : m_manifest.attr("get_engine_gems")())
{
for (auto path : m_manifest.attr("get_engine_gems")())
{
gems.push_back(GemInfoFromPath(path));
}
});
gems.push_back(GemInfoFromPath(path));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -485,13 +486,13 @@ namespace O3DE::ProjectManager
QVector<GemInfo> gems;
auto result = ExecuteWithLockErrorHandling([&]
{
pybind11::str pyProjectPath = projectPath.toStdString();
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
{
pybind11::str pyProjectPath = projectPath.toStdString();
for (auto path : m_manifest.attr("get_all_gems")(pyProjectPath))
{
gems.push_back(GemInfoFromPath(path));
}
});
gems.push_back(GemInfoFromPath(path));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -506,12 +507,12 @@ namespace O3DE::ProjectManager
// Retrieve the path to the cmake file that lists the enabled gems.
pybind11::str enabledGemsFilename;
auto result = ExecuteWithLockErrorHandling([&]
{
const pybind11::str pyProjectPath = projectPath.toStdString();
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
pybind11::none(), // project_name
pyProjectPath); // project_path
});
{
const pybind11::str pyProjectPath = projectPath.toStdString();
enabledGemsFilename = m_cmake.attr("get_enabled_gem_cmake_file")(
pybind11::none(), // project_name
pyProjectPath); // project_path
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -520,13 +521,13 @@ namespace O3DE::ProjectManager
// Retrieve the actual list of names from the cmake file.
QVector<AZStd::string> gemNames;
result = ExecuteWithLockErrorHandling([&]
{
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
for (auto gemName : pyGemNames)
{
const auto pyGemNames = m_cmake.attr("get_enabled_gems")(enabledGemsFilename);
for (auto gemName : pyGemNames)
{
gemNames.push_back(Py_To_String(gemName));
}
});
gemNames.push_back(Py_To_String(gemName));
}
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
@@ -540,13 +541,13 @@ namespace O3DE::ProjectManager
bool registrationResult = false;
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(pybind11::none(), projectPath);
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
return result && registrationResult;
}
@@ -556,30 +557,30 @@ namespace O3DE::ProjectManager
bool registrationResult = false;
bool result = ExecuteWithLock(
[&]
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(
pybind11::none(), // engine_path
projectPath, // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
pybind11::none(), // default_projects_folder
pybind11::none(), // default_gems_folder
pybind11::none(), // default_templates_folder
pybind11::none(), // default_restricted_folder
pybind11::none(), // external_subdir_engine_path
pybind11::none(), // external_subdir_project_path
true, // remove
false // force
{
pybind11::str projectPath = path.toStdString();
auto pythonRegistrationResult = m_register.attr("register")(
pybind11::none(), // engine_path
projectPath, // project_path
pybind11::none(), // gem_path
pybind11::none(), // external_subdir_path
pybind11::none(), // template_path
pybind11::none(), // restricted_path
pybind11::none(), // repo_uri
pybind11::none(), // default_engines_folder
pybind11::none(), // default_projects_folder
pybind11::none(), // default_gems_folder
pybind11::none(), // default_templates_folder
pybind11::none(), // default_restricted_folder
pybind11::none(), // external_subdir_engine_path
pybind11::none(), // external_subdir_project_path
true, // remove
false // force
);
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
// Returns an exit code so boolify it then invert result
registrationResult = !pythonRegistrationResult.cast<bool>();
});
return result && registrationResult;
}
@@ -637,12 +638,12 @@ namespace O3DE::ProjectManager
try
{
// required
gemInfo.m_name = Py_To_String(data["gem_name"]);
gemInfo.m_name = Py_To_String(data["gem_name"]);
// optional
gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name);
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
gemInfo.m_summary = Py_To_String_Optional(data, "Summary", "");
gemInfo.m_version = Py_To_String_Optional(data, "Version", "");
if (data.contains("Tags"))
{
@@ -673,7 +674,7 @@ namespace O3DE::ProjectManager
try
{
projectInfo.m_projectName = Py_To_String(projectData["project_name"]);
projectInfo.m_displayName = Py_To_String_Optional(projectData,"display_name", projectInfo.m_projectName);
projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName);
}
catch ([[maybe_unused]] const std::exception& e)
{
@@ -715,33 +716,33 @@ namespace O3DE::ProjectManager
AZ::Outcome<void, AZStd::string> PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath)
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_enableGemProject.attr("enable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
m_enableGemProject.attr("enable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
);
});
});
}
AZ::Outcome<void, AZStd::string> PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath)
{
return ExecuteWithLockErrorHandling([&]
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
{
pybind11::str pyGemPath = gemPath.toStdString();
pybind11::str pyProjectPath = projectPath.toStdString();
m_disableGemProject.attr("disable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
m_disableGemProject.attr("disable_gem_in_project")(
pybind11::none(), // gem name not needed as path is provided
pyGemPath,
pybind11::none(), // project name not needed as path is provided
pyProjectPath
);
});
});
}
bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo)
@@ -761,8 +762,8 @@ namespace O3DE::ProjectManager
{
// required
templateInfo.m_displayName = Py_To_String(data["display_name"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
templateInfo.m_name = Py_To_String(data["template_name"]);
templateInfo.m_summary = Py_To_String(data["summary"]);
// optional
if (data.contains("canonical_tags"))
@@ -794,7 +795,7 @@ namespace O3DE::ProjectManager
QVector<ProjectTemplateInfo> templates;
bool result = ExecuteWithLock([&] {
for (auto path : m_manifest.attr("get_project_templates")())
for (auto path : m_manifest.attr("get_templates_for_project_creation")())
{
templates.push_back(ProjectTemplateInfoFromPath(path));
}
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a476e99b55cf2a76fef6775c5a57dad29f8ffcb942c625bab04c89051a72a560
size 62626
oid sha256:838830c99f344f5b68e5e85c9bc52751350caf48e662c9c2b767ab77039bbd8f
size 103472
@@ -91,7 +91,8 @@ AZ_POP_DISABLE_WARNING
#include <ScriptCanvas/Variable/VariableBus.h>
#include <ScriptCanvas/Libraries/UnitTesting/UnitTestingLibrary.h>
////
AZ_CVAR(bool, g_disableDeprecatedNodeUpdates, false, {}, AZ::ConsoleFunctorFlags::Null,
"Disables automatic update attempts of deprecated nodes, so that graphs that require and update can be viewed in their original form");
namespace EditorGraphCpp
{
@@ -3642,7 +3643,7 @@ namespace ScriptCanvasEditor
if (scriptCanvasNode)
{
if (scriptCanvasNode->IsDeprecated())
if (scriptCanvasNode->IsDeprecated() && !g_disableDeprecatedNodeUpdates)
{
ScriptCanvas::NodeConfiguration nodeConfig = scriptCanvasNode->GetReplacementNodeConfiguration();
if (nodeConfig.IsValid())
@@ -22,6 +22,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#pragma once
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <ScriptCanvas/Core/Nodeable.h>
#include <ScriptCanvas/Core/NodeableNode.h>
@@ -43,6 +48,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{% endif %}
{% endif %}
{%- set attribute_Base = Class.attrib['Base'] %}
{% if not Class.attrib['Base'] is defined %}
{% set attribute_Base = "ScriptCanvas::Nodeable" %}
{% endif %}
{% if attribute_Namespace is defined %}
namespace {{attribute_Namespace}}
{
@@ -66,6 +76,9 @@ namespace {{attribute_Namespace}}
public: \
AZ_RTTI({{className}}, "{{nodeableClassName|createHashGuid}}"{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}); \
static void Reflect(AZ::ReflectContext* reflection); \
static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %} \
static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %} \
static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{className}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %} \
static const char* GetDescription() { return "{{ macros.GetAttributeAsString(Class.attrib, 'Description') }}"; } \
ScriptCanvas::NodePropertyInterface* GetPropertyInterface(AZ::Crc32 propertyId) override; \
bool IsActive() const override { return false; } \
@@ -83,13 +96,33 @@ public: \
AZ_COMPONENT({{nodeableNodeName}}, {% if Class.attrib['NodeableUuid'] is defined %}"{{Class.attrib['NodeableUuid']}}"{% else %}"{{nodeableNodeName|createHashGuid}}"{% endif %}, ScriptCanvas::Nodes::NodeableNode);
static void Reflect(AZ::ReflectContext* context);
static void ExtendReflectionSerialize([[maybe_unused]] AZ::SerializeContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionSerialize'] is defined %};{% else %}{}{% endif %}
static void ExtendReflectionEdit([[maybe_unused]] AZ::EditContext::ClassBuilder* builder){% if Class.attrib['ExtendReflectionEdit'] is defined %};{% else %}{}{% endif %}
static void ExtendReflectionBehavior([[maybe_unused]] AZ::BehaviorContext::ClassBuilder<{{nodeableNodeName}}>* builder){% if Class.attrib['ExtendReflectionBehavior'] is defined %};{% else %}{}{% endif %}
void ConfigureSlots() override;
{% if Class.attrib['ExtendConfigureSlots'] is defined %}
void ExtendConfigureSlots([[maybe_unused]] SlotExecution::Ins& ins, [[maybe_unused]] SlotExecution::Outs& latents);
{% else %}
/* no slot configuration extension, Use Class attribute 'ExtendConfigureSlots' to extend them */
{% endif %}
void ConfigureVisualExtensions() override;
size_t GenerateFingerprint() const override;
{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %}
{% if Class.attrib['EntryPoint'] is defined and Class.attrib['EntryPoint'] == "true" %}
bool IsEntryPoint() const override { return true; }
{% endif %}
{% endif %}
{{nodeableNodeName}}();
{{Class.attrib['NodeDeclarations']}}
};
}
{% endif %}
@@ -100,6 +133,5 @@ public: \
{{ macros.ReportErrors() }}
{% endfor %}
{% endfor %}
{% endfor %}
@@ -19,15 +19,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <ScriptCanvas/Core/Contracts.h>
#include <ScriptCanvas/Core/NodeableNode.h>
#include <ScriptCanvas/Core/SlotExecutionMap.h>
#include <ScriptCanvas/Grammar/ParsingUtilities.h>
#include <ScriptCanvas/Utils/VersionConverters.h>
{% for xml in dataFiles %}
@@ -47,13 +42,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{%- set attribute_Category = Class.attrib['Category'] %}
{%- set attribute_Uuid = Class.attrib['Uuid'] %}
{%- set attribute_Icon = Class.attrib['Icon'] %}
{%- set attribute_Base = Class.attrib['Base'] %}
{%- set attribute_GeneratePropertyFriend = Class.attrib['GeneratePropertyFriend'] %}
{%- set attribute_Version = Class.attrib['Version'] %}
{%- set attribute_VersionConverter = Class.attrib['VersionConverter'] %}
{%- set attribute_EventHandler = Class.attrib['EventHandler'] %}
{%- set attribute_Deprecated = Class.attrib['Deprecated'] %}
{%- set attribute_Base = Class.attrib['Base'] %}
{% if not Class.attrib['Base'] is defined %}
{% set attribute_Base = "ScriptCanvas::Nodeable" %}
{% endif %}
{% set attribute_Namespace = undefined %}
{%- if Class.attrib['Namespace'] is defined %}
{% if Class.attrib['Namespace'] != "None" %}
@@ -95,19 +95,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
{{CollectDisplayGroups('Output')}}
{{CollectDisplayGroups('Parameter')}}
{# FOR DEBUGGING / DIAGNOSTIC
// Standalone (No DisplayGroup) {{ global_standaloneTagMap }}
{% for key, value in global_standaloneTagMap.items() %}
// {{key}} : {{value}}
{% endfor %}
// DisplayGrouped {{ global_displayGroupMap }}
{% for key, value in global_displayGroupMap.items() %}
// {{key}} : {{value}}
{% endfor %}
#}
{# ----------------------------------------------------------------------------------------- #}
{% if attribute_Namespace is defined %}
@@ -115,33 +102,6 @@ namespace {{attribute_Namespace}}
{
{% endif %}
{# Standard "In" function }
{{nodemacro.FunctionSignature(attribute_QualifiedName, Class)}}
{
{%- for parameter in Class.findall('Parameter') -%}
{% if parameter.attrib['Input'] is defined and parameter.attrib['Input'] == "True" %}
// this->{{parameter.attrib['Name']}} = arg{{loop.index0}};
{%- endif -%}
{% endfor %}
{% set returnNames = [] %}
{% set returnTypes = [] %}
{%- for return in Class.findall('Parameter') -%}
{%- if return.attrib['Output'] is defined and return.attrib['Output'] == "True" -%}
{% if returnTypes.append(return.attrib['Type']) %}{% endif %}
{% if returnNames.append("this->" + return.attrib['Name']) %}{% endif %}
{%- endif -%}
{%- endfor -%}
{% if returnNames|length() == 1 %}
return {{returnNames[0]}};
{% elif returnNames|length() > 1 %}
return AZStd::tuple<{{returnTypes|join(", ")}}>({{returnNames|join(", ")}});
{% endif %}
}
#}
{%- set nodeableNodeName = attribute_Name + 'Node' %}
{% set list_outputs = [] %}
{% for output in Class.iter('Output') %}
@@ -156,43 +116,81 @@ return {{returnNames[0]}};
{% for item in Class.iter('Output') %}
{% if item.attrib['DisplayGroup'] is defined %}{% set displayGroup = item.attrib['DisplayGroup'] %}{% endif %}
{% endfor %}
{% set branches = [] %}
{% for method in Class.findall('Input') %}
{% for branch in method.findall('Branch') %}
{% if branches.append(branch) %}{% endif %}
{% endfor %}
{% endfor %}
{# ExecutionOuts #}
// ExecutionOuts begin
{{ nodemacro.ExecutionOutDefinitions(Class, attribute_QualifiedName)}}
{% if not Class.attrib['ExtendConfigureSlots'] is defined %} size_t {{attribute_QualifiedName}}::GetRequiredOutCount() const { return {{Class.findall('Output')|length + branches|length}}; }{% endif %}
// ExecutionOuts end
{# Reflect #}
{% if Class.attrib['ExtendReflectionSerialize'] is defined %}
{% set ExtendReflectionSerialize = "defined" %}
{% set preSerialize = "serializeBuilder" %}
{% set postSerialize = ";" %}
{% else %}
{% set preSerialize = "" %}
{% set postSerialize = "" %}
{% endif %}
{% if Class.attrib['ExtendReflectionEdit'] is defined %}
{% set ExtendReflectionEdit = "defined" %}
{% set preEdit = "editorBuilder" %}
{% set postEdit = ";" %}
{% else %}
{% set preEdit = "" %}
{% set postEdit = "" %}
{% endif %}
{% if Class.attrib['ExtendReflectionBehavior'] is defined %}
{% set ExtendReflectionBehavior = "defined" %}
{% set preBehavior = "behaviorBuilder" %}
{% set postBehavior = ";" %}
{% else %}
{% set preBehavior = "" %}
{% set postBehavior = "" %}
{% endif %}
void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
{
using namespace ScriptCanvas;
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>()
{% if ExtendReflectionSerialize is defined %} auto {{preSerialize}} = {% else %} {% endif %}serializeContext->Class<{{ attribute_Name }}{% if attribute_Base is defined %}, {{ attribute_Base }}{% endif %}>(){{postSerialize}}
{% if attribute_EventHandler is defined %}
->EventHandler<{{ attribute_EventHandler }}>()
{{preSerialize}}->EventHandler<{{ attribute_EventHandler }}>(){{postSerialize}}
{% endif %}
{# Serialized Properties #}
{% for Property in Class.iter('Property') %}
{% set property_Name = Property.attrib['Name'] %}
->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }})
{{preSerialize}}->Field("{{ property_Name }}", &{{ attribute_Name }}::{{ property_Name | replace(' ','') }}){{postSerialize}}
{% endfor %}
;
{% if ExtendReflectionSerialize is defined %}
ExtendReflectionSerialize(&{{preSerialize}});
{% endif %}
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
{% if ExtendReflectionEdit is defined %} auto {{preEdit}} = {% else %} {% endif %}editContext->Class<{{ attribute_QualifiedName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}}
{{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}}
{% if attribute_Category is defined %}
->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Category, "{{ attribute_Category }}"){{postEdit}}
{%- endif %}
{% if attribute_Icon is defined %}
->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Icon, "{{ attribute_Icon }}"){{postEdit}}
{%- endif %}
{% if attribute_Deprecated is defined %}
->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}")
{{preEdit}}->Attribute(AZ::Edit::Attributes::Deprecated, "{{ attribute_Deprecated }}"){{postEdit}}
{%- endif %}
{% set uihandler = 'AZ::Edit::UIHandlers::Default' %}
{% for item in Class.iter('Property') %}
@@ -203,29 +201,34 @@ void {{attribute_QualifiedName}}::Reflect(AZ::ReflectContext* context)
{% if item.attrib['Description'] is defined %}
{% set description = item.attrib['Description'] %}
{% endif %}
// {{ item.attrib['Name'] }}
->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}")
// {{ item.attrib['Name'] }}
{{preEdit}}->DataElement({{ uihandler }}, &{{ attribute_Name }}::{{ item.attrib['Name'] }}, "{{ item.attrib['Name'] }}", "{{ description }}"){{postEdit}}
{% for EditAttribute in item.iter('EditAttribute') %}
->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }})
{{preEdit}}->Attribute({{ EditAttribute.attrib['Key'] }}, {{ EditAttribute.attrib['Value'] }}){{postEdit}}
{% endfor %}
{% endfor %}
;
{% if ExtendReflectionEdit is defined %}
ExtendReflectionEdit(&{{preEdit}});
{% endif %}
}
}
// Behavior Context Reflection
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List)
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
{% if ExtendReflectionBehavior is defined %} auto {{preBehavior}} = {% else %} {% endif %}behaviorContext->Class<{{ attribute_Name }}>("{{ attribute_Name }}"){{postBehavior}}
{{preBehavior}}->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::List){{postBehavior}}
{{preBehavior}}->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common){{postBehavior}}
{% for inputMethod in Class.iter('Input') %}
{% set methodName = inputMethod.attrib['Name'] %}
// {{ inputMethod.attrib['Name'] }}
->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }})
{{preBehavior}}->Method(Grammar::ToIdentifier("{{ macros.SlotName(methodName) }}").c_str(), &{{ attribute_Name }}::{{ macros.CleanName(methodName) }}){{postBehavior}}
{% endfor %}
;
{% if ExtendReflectionBehavior is defined %}
ExtendReflectionBehavior(&{{preBehavior}});
{% endif %}
}
}
@@ -253,27 +256,32 @@ Nodes::{{ nodeableNodeName }}::{{ nodeableNodeName }}()
{# NodeableNode Reflection #}
void Nodes::{{ nodeableNodeName }}::Reflect(AZ::ReflectContext* context)
{
{{ attribute_QualifiedName }}::Reflect(context);
// Serialization Context Reflection
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>()
{%if ExtendReflectionSerialize is defined%}auto {{preSerialize}} = {%endif%}serializeContext->Class<{{ nodeableNodeName }}, NodeableNode>(){{postSerialize}}
{% if attribute_Version is defined %}
->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %})
{{preSerialize}}->Version({{ attribute_Version }}{% if attribute_VersionConverter is defined %}, &{{ attribute_VersionConverter }}{% endif %}){{postSerialize}}
{% else %}
->Version(0)
{{preSerialize}}->Version(0){{postSerialize}}
{% endif %}
;
{% if ExtendReflectionSerialize is defined %}
ExtendReflectionSerialize(&{{preSerialize}});
{% endif %}
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
{% if ExtendReflectionEdit is defined %}auto {{preEdit}} = {%endif%}editContext->Class<{{ nodeableNodeName }}>("{{ attribute_PreferredClassName }}", "{{ attribute_Description }}"){{postEdit}}
{{preEdit}}->ClassElement(AZ::Edit::ClassElements::EditorData, ""){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly){{postEdit}}
{{preEdit}}->Attribute(AZ::Edit::Attributes::AutoExpand, true){{postEdit}}
;
{% if ExtendReflectionEdit is defined %}
ExtendReflectionEdit(&{{preEdit}});
{% endif %}
}
}
}
@@ -324,6 +332,7 @@ void Nodes::{{ nodeableNodeName }}::ConfigureVisualExtensions()
RegisterExtension(visualExtensions);
}
{% endfor %}
OnConfigureVisualExtensions();
}
{# ConfigureSlots #}
@@ -457,6 +466,9 @@ void Nodes::{{ nodeableNodeName }}::ConfigureSlots()
{% endfor %}
#}
{% if Class.attrib['ExtendConfigureSlots'] is defined %}
ExtendConfigureSlots(ins, outs);
{% endif %}
// Generate the execution map
m_slotExecutionMap = SlotExecution::Map(AZStd::move(ins), AZStd::move(outs));
@@ -349,7 +349,4 @@ void {{qualifiedName}}::Call{{CleanName(outName)}}({{ExecutionOutReturnDefinitio
{%- 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 %}
{% endmacro %}
@@ -250,7 +250,6 @@ namespace ScriptCanvas
};
using ScriptCanvasSettingsRequestBus = AZ::EBus<ScriptCanvasSettingsRequests>;
}
namespace AZStd
@@ -1476,9 +1476,11 @@ namespace ScriptCanvas
const void* Datum::GetValueAddress() const
{
return m_type.GetType() != Data::eType::BehaviorContextObject
? AZStd::any_cast<void>(&m_storage)
: (*AZStd::any_cast<BehaviorContextObjectPtr>(&m_storage))->Get();
return !m_storage.empty()
? m_type.GetType() != Data::eType::BehaviorContextObject
? AZStd::any_cast<void>(&m_storage)
: (*AZStd::any_cast<BehaviorContextObjectPtr>(&m_storage))->Get()
: nullptr;
}
bool Datum::Initialize(const Data::Type& type, eOriginality originality, const void* source, const AZ::Uuid& sourceTypeID)
@@ -389,7 +389,7 @@ namespace ScriptCanvas
bool Datum::Empty() const
{
return GetValueAddress() == nullptr;
return m_storage.empty() || GetValueAddress() == nullptr;
}
template<typename t_Value>
@@ -3577,12 +3577,12 @@ namespace ScriptCanvas
}
if (targetSlotType == CombinedSlotType::DataOut
&& executionSlot.GetType() == CombinedSlotType::ExecutionIn
&& executionInCount > 1)
&& executionSlot.GetType() == CombinedSlotType::ExecutionIn
&& executionInCount > 1)
{
if (!executionChildSlot || executionChildSlot->GetType() != CombinedSlotType::ExecutionOut)
{
return AZ::Failure(AZStd::string("Data out by ExcutionIn must have child out slot"));
return AZ::Failure(AZStd::string("Data out by ExecutionIn must have child out slot"));
}
}
@@ -3626,6 +3626,11 @@ namespace ScriptCanvas
return {};
}
Grammar::MultipleFunctionCallFromSingleSlotInfo Node::GetMultipleFunctionCallFromSingleSlotInfo([[maybe_unused]] const Slot& slot) const
{
return {};
}
VariableId Node::GetVariableIdRead(const Slot*) const
{
return {};
@@ -715,6 +715,8 @@ namespace ScriptCanvas
virtual PropertyFields GetPropertyFields() const;
virtual Grammar::MultipleFunctionCallFromSingleSlotInfo GetMultipleFunctionCallFromSingleSlotInfo(const Slot& slot) const;
virtual VariableId GetVariableIdRead(const Slot*) const;
virtual VariableId GetVariableIdWritten(const Slot*) const;
@@ -73,6 +73,8 @@ namespace ScriptCanvas
void ConfigureSlots() override;
virtual void OnConfigureVisualExtensions() {}
AZ::Outcome<const AZ::BehaviorClass*, AZStd::string> GetBehaviorContextClass() const;
ConstSlotsOutcome GetBehaviorContextOutName(const Slot& inSlot) const;
@@ -46,6 +46,8 @@ namespace ScriptCanvas
const AZStd::unordered_map<AZStd::string, AZStd::pair<SlotId, SlotId>>& GetPropertyNameSlotMap() const;
AZ_INLINE AZ::Outcome<DependencyReport, void> GetDependencies() const override { return AZ::Success(DependencyReport{}); }
~PureData() override;
protected:
@@ -2249,6 +2249,10 @@ namespace ScriptCanvas
}
#endif
AddAllVariablesPreParse();
if (!IsErrorFree())
{
return;
}
for (auto& nodeEntity : m_source.m_graphData->m_nodes)
{
@@ -2270,6 +2274,16 @@ namespace ScriptCanvas
{
AddError(nullptr, ValidationConstPtr(aznew NullEntityInGraph()));
}
if (!IsErrorFree())
{
return;
}
}
if (!IsErrorFree())
{
return;
}
ParseAutoConnectedEBusHandlerVariables();
@@ -2638,7 +2652,8 @@ namespace ScriptCanvas
AZStd::vector<VariableId> inputVariableIds;
AZStd::unordered_map<VariableId, Grammar::VariableConstPtr> inputVariablesById;
for (auto variable : GetVariables())
auto& variables = GetVariables();
for (auto variable : variables)
{
auto constructionRequirement = ParseConstructionRequirement(variable);
@@ -2655,18 +2670,30 @@ namespace ScriptCanvas
case VariableConstructionRequirement::InputNodeable:
{
if (variable->m_datum.Empty())
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "Empty nodeable datum in variable, probably due to a problem with azrtti declarations"));
break;
}
// I solemnly swear no harm shall come to the nodeable
const Nodeable* nodeableSource = reinterpret_cast<const Nodeable*>(variable->m_datum.GetAsDanger());
AZ_Assert(nodeableSource != nullptr, "the must be a raw nodeable held by this pointer");
AZ_Assert(azrtti_typeid(nodeableSource) != azrtti_typeid<Nodeable>(), "type problem with nodeable");
if (!nodeableSource)
{
AddError(nullptr, aznew Internal::ParseError(AZ::EntityId{}, "No raw nodeable held by variable"));
break;
}
nodeablesById.push_back({ variable->m_nodeableNodeId, const_cast<Nodeable*>(nodeableSource) });
}
break;
case VariableConstructionRequirement::InputVariable:
{
inputVariableIds.push_back(variable->m_sourceVariableId);
inputVariablesById.insert({ variable->m_sourceVariableId, variable });
auto variableID = variable->m_sourceVariableId.IsValid() ? variable->m_sourceVariableId : VariableId::MakeVariableId();
inputVariableIds.push_back(variableID);
inputVariablesById.insert({ variableID, variable });
// sort revealed a datum copy issue: type is not preserved, workaround below
// m_runtimeInputs.m_variables.emplace_back(variable->m_sourceVariableId, variable->m_datum);
}
@@ -4310,6 +4337,7 @@ namespace ScriptCanvas
{
if (auto variable = FindVariable(execution->GetNodeId()))
{
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4327,6 +4355,7 @@ namespace ScriptCanvas
{
auto variable = AZStd::make_shared<Variable>();
variable->m_datum = Datum(eventHandling->m_handlerName);
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4338,6 +4367,7 @@ namespace ScriptCanvas
{
if (auto variable = FindVariable(execution->GetNodeId()))
{
execution->MarkInputHasThisPointer();
execution->AddInput({ nullptr, variable, DebugDataSource::FromInternal() });
}
else
@@ -4367,12 +4397,159 @@ namespace ScriptCanvas
void AbstractCodeModel::ParseMultiExecutionPost(ExecutionTreePtr execution)
{
ParsePropertyExtractionsPost(execution);
ParseMultipleFunctionCallPost(execution);
}
void AbstractCodeModel::ParseMultiExecutionPre(ExecutionTreePtr execution)
{
ParsePropertyExtractionsPre(execution);
}
}
void AbstractCodeModel::ParseMultipleFunctionCallPost(ExecutionTreePtr execution)
{
auto& id = execution->GetId();
MultipleFunctionCallFromSingleSlotInfo info = id.m_node->GetMultipleFunctionCallFromSingleSlotInfo(*id.m_slot);
if (info.functionCalls.empty())
{
return;
}
auto parent = execution->ModParent();
if (!parent)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), "Null parent in MultipleFunctionCall"));
return;
}
size_t indexInParentCall = parent->FindChildIndex(execution);
if (indexInParentCall >= parent->GetChildrenCount())
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNoChildren));
return;
}
ExecutionChild* executionChildInParent = &parent->ModChild(indexInParentCall);
const size_t executionInputCount = execution->GetInputCount();
const size_t thisInputOffset = execution->InputHasThisPointer() ? 1 : 0;
// the original index has ALL the input from the slots on the node
// create multiple calls with separate function call nodes, but ONLY take the inputs required
// as indicated by the function call info
AZStd::unordered_set<const Slot*> usedSlots;
bool variadicIsFound = false;
auto createChild = [&](auto parentCall, ExecutionChild* childInParent, auto& functionCallInfo)
{
auto child = CreateChild(parentCall, id.m_node, id.m_slot);
child->SetSymbol(Symbol::FunctionCall);
child->SetName(functionCallInfo.functionName);
child->SetNameLexicalScope(functionCallInfo.lexicalScope);
childInParent->m_execution = child;
return child;
};
auto addThisInput = [&](auto functionCall)
{
if (thisInputOffset != 0)
{
if (executionInputCount == 0)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInputForThis));
return;
}
const ExecutionInput& input = execution->GetInput(0);
usedSlots.insert(input.m_slot);
functionCall->AddInput(input);
}
};
auto addSlotInput = [&](auto functionCall, size_t inputIndex)
{
if (inputIndex >= executionInputCount)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput));
return;
}
const ExecutionInput& input = execution->GetInput(inputIndex);
if (usedSlots.contains(input.m_slot))
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotNotEnoughInput));
return;
}
usedSlots.insert(input.m_slot);
if (input.m_value->m_source == execution)
{
input.m_value->m_source = functionCall;
}
functionCall->AddInput(input);
};
auto addCall = [&](auto& functionCallInfo, auto childInParent, size_t startingIndex, size_t sentinel, size_t variadicOffset = 0)
{
auto child = createChild(parent, childInParent, functionCallInfo);
addThisInput(child);
for (size_t index = startingIndex; index < sentinel; ++index)
{
const size_t inputIndex = index + thisInputOffset + variadicOffset;
addSlotInput(child, inputIndex);
}
child->AddChild({});
childInParent = &child->ModChild(0);
return AZStd::make_pair(childInParent, child);
};
// loop through each call...
for (auto& functionCallInfo : info.functionCalls)
{
// ...first add any pre-variadic calls, using the starting index and the number of args, since they could come in any order, not input slot order...
if (!functionCallInfo.isVariadic)
{
AZStd::pair<ExecutionChild*, ExecutionTreePtr> childInParentAndParent = addCall(functionCallInfo, executionChildInParent, functionCallInfo.startingIndex, functionCallInfo.startingIndex + functionCallInfo.numArguments);
executionChildInParent = childInParentAndParent.first;
parent = childInParentAndParent.second;
}
else
{
// ...then add only one variadic call if there is one...
if (variadicIsFound)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotMultipleVariadic));
return;
}
variadicIsFound = true;
const size_t sentinel = executionInputCount == 0 ? 0 : executionInputCount - thisInputOffset;
// ... by looping through the remaining slots, striding by functionCallInfo.numArguments, making repeated calls to the function
for (size_t slotInputIndex = functionCallInfo.startingIndex; slotInputIndex < sentinel; slotInputIndex += functionCallInfo.numArguments)
{
AZStd::pair<ExecutionChild*, ExecutionTreePtr> childInParentAndParent = addCall(functionCallInfo, executionChildInParent, 0, functionCallInfo.numArguments, slotInputIndex);
executionChildInParent = childInParentAndParent.first;
parent = childInParentAndParent.second;
}
}
}
if (info.errorOnUnusedSlot && usedSlots.size() != executionInputCount)
{
AddError(execution, aznew Internal::ParseError(id.m_node->GetEntityId(), ParseErrors::MultipleFunctionCallFromSingleSlotUnused));
}
// parent now refers to the last child call created
parent->SwapChildren(execution);
execution->Clear();
}
void AbstractCodeModel::ParseNodelingVariables(const Node& node, NodelingType nodelingType)
{
@@ -5146,6 +5323,6 @@ namespace ScriptCanvas
return type == Data::eType::BehaviorContextObject;
}
}
}
}
}
@@ -387,6 +387,8 @@ namespace ScriptCanvas
void ParseMultiExecutionPre(ExecutionTreePtr execution);
void ParseMultipleFunctionCallPost(ExecutionTreePtr execution);
void ParseNodelingVariables(const Node& node, NodelingType nodelingType);
void ParseOperatorArithmetic(ExecutionTreePtr execution);
@@ -1132,14 +1132,14 @@ namespace ScriptCanvas
}
else if (variable->m_isExposedToConstruction)
{
if (variable->m_sourceVariableId.IsValid())
{
return VariableConstructionRequirement::InputVariable;
}
else if (variable->m_nodeableNodeId.IsValid())
if (variable->m_nodeableNodeId.IsValid())
{
return VariableConstructionRequirement::InputNodeable;
}
else if (variable->m_sourceVariableId.IsValid())
{
return VariableConstructionRequirement::InputVariable;
}
else
{
AZ_Assert(false, "A member variable in the model has no valid id");
@@ -164,6 +164,34 @@ namespace ScriptCanvas
virtual void PostParseExecutionTreeBody(AbstractCodeModel& /*model*/, ExecutionTreePtr /*execution*/) {}
};
// for now, no return values supported
struct MultipleFunctionCallFromSingleSlotEntry
{
AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotEntry, "{360A23A3-C490-4047-B71E-64E290E441D3}");
AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotEntry, AZ::SystemAllocator, 0);
bool isVariadic = false;
AZStd::string functionName;
LexicalScope lexicalScope;
size_t numArguments = 0; // stride in case isVariadic == true
size_t startingIndex = 0; // the index of the slot order
};
// for now, no return values supported
struct MultipleFunctionCallFromSingleSlotInfo
{
AZ_TYPE_INFO(MultipleFunctionCallFromSingleSlotInfo, "{DF51F08A-8B28-4851-9888-9AB7CC0B90D2}");
AZ_CLASS_ALLOCATOR(MultipleFunctionCallFromSingleSlotInfo, AZ::SystemAllocator, 0);
// this could likely be implemented, but needs care to duplicate input that the execution-slot created
// bool errorOnReusedSlot = false;
bool errorOnUnusedSlot = false;
// calls are executed in the order they arrive in the vector
AZStd::vector<MultipleFunctionCallFromSingleSlotEntry> functionCalls;
};
struct NodeableParse
: public AZStd::enable_shared_from_this<NodeableParse>
{
@@ -313,6 +313,11 @@ namespace ScriptCanvas
return !m_returnValues.empty();
}
bool ExecutionTree::InputHasThisPointer() const
{
return m_inputHasThisPointer;
}
bool ExecutionTree::IsInfiniteLoopDetectionPoint() const
{
return m_isInfiniteLoopDetectionPoint;
@@ -377,6 +382,11 @@ namespace ScriptCanvas
m_isInfiniteLoopDetectionPoint = true;
}
void ExecutionTree::MarkInputHasThisPointer()
{
m_inputHasThisPointer = true;
}
void ExecutionTree::MarkInputOutputPreprocessed()
{
m_isInputOutputPreprocessed = true;
@@ -587,6 +597,32 @@ namespace ScriptCanvas
m_symbol = val;
}
}
void ExecutionTree::SwapChildren(ExecutionTreePtr execution)
{
if (execution)
{
m_children.swap(execution->m_children);
for (auto& child : m_children)
{
if (child.m_execution)
{
child.m_execution->SetParent(shared_from_this());
}
}
for (auto& orphan : execution->m_children)
{
if (orphan.m_execution)
{
orphan.m_execution->SetParent(execution);
}
}
}
else
{
ClearChildren();
}
}
}
}
@@ -190,6 +190,8 @@ namespace ScriptCanvas
bool HasReturnValues() const;
bool InputHasThisPointer() const;
bool IsInfiniteLoopDetectionPoint() const;
void InsertChild(size_t index, const ExecutionChild& child);
@@ -208,6 +210,8 @@ namespace ScriptCanvas
void MarkInfiniteLoopDetectionPoint();
void MarkInputHasThisPointer();
void MarkInputOutputPreprocessed();
void MarkInternalOut();
@@ -262,6 +266,8 @@ namespace ScriptCanvas
void SetSymbol(Symbol val);
void SwapChildren(ExecutionTreePtr execution);
private:
// the (possible) slot(s) through which execution exited, along with associated output
AZStd::vector<ExecutionChild> m_children;
@@ -275,6 +281,8 @@ namespace ScriptCanvas
bool m_isInfiniteLoopDetectionPoint = false;
bool m_inputHasThisPointer = false;
bool m_isInputOutputPreprocessed = false;
bool m_isInternalOut = false;
@@ -57,6 +57,12 @@ namespace ScriptCanvas
constexpr const char* MissingVariableForEBusHandlerAddress = "missing variable for ebus handler address";
constexpr const char* MissingVariableForEBusHandlerAddressConnected = "missing variable for ebus handler address";
constexpr const char* MultipleExecutionOutConnections = "This node has multiple, unordered execution Out connections";
constexpr const char* MultipleFunctionCallFromSingleSlotMultipleVariadic = "Only one variadic call (the last one) is supported in the multi-call per single slot.";
constexpr const char* MultipleFunctionCallFromSingleSlotNoChildren = "Node missing from parent children.";
constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInput = "Not enough input to support multi call input information.";
constexpr const char* MultipleFunctionCallFromSingleSlotNotEnoughInputForThis = "Node doesn't have enough input for a parsed this pointer.";
constexpr const char* MultipleFunctionCallFromSingleSlotReused = "Multiple function slot reused an input slot";
constexpr const char* MultipleFunctionCallFromSingleSlotUnused = "Multiple function slot left an input slot unused.";
constexpr const char* MultipleSimulaneousInputValues = "Multiple values routed to the same single input with no way to discern which to take.";
constexpr const char* MultipleStartNodes = "Multiple Start nodes in a single graph. Only one is allowed.";
constexpr const char* NoChildrenAfterRoot = "No children after parsing function root";
+64 -33
View File
@@ -310,64 +310,95 @@ def get_project_external_subdirectories(project_path: pathlib.Path) -> list:
project_object['external_subdirectories'])) if 'external_subdirectories' in project_object else []
def get_project_templates(project_path: pathlib.Path) -> list:
project_object = get_project_json_data(project_path=project_path)
return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(),
project_object['templates']))
def get_project_restricted(project_path: pathlib.Path) -> list:
project_object = get_project_json_data(project_path=project_path)
return list(map(lambda rel_path: (pathlib.Path(project_path) / rel_path).as_posix(),
project_object['restricted'])) if 'restricted' in project_object else []
# Combined manifest queries
def get_all_projects() -> list:
projects_data = set(get_projects())
projects_data.update(get_engine_projects())
return list(projects_data)
projects_data = get_projects()
projects_data.extend(get_engine_projects())
# Remove duplicates from the list
return list(dict.fromkeys(projects_data))
def get_all_gems(project_path: pathlib.Path = None) -> list:
gems_data = set(get_gems())
gems_data.update(get_engine_gems())
gems_data = get_gems()
gems_data.extend(get_engine_gems())
if project_path:
gems_data.update(get_project_gems(project_path))
return list(gems_data)
gems_data.extend(get_project_gems(project_path))
return list(dict.fromkeys(gems_data))
def get_all_external_subdirectories(project_path: pathlib.Path = None) -> list:
external_subdirectories_data = set(get_external_subdirectories())
external_subdirectories_data.update(get_engine_external_subdirectories())
external_subdirectories_data = get_external_subdirectories()
external_subdirectories_data.extend(get_engine_external_subdirectories())
if project_path:
external_subdirectories_data.update(get_project_external_subdirectories(project_path))
return list(templates_data)
external_subdirectories_data.extend(get_project_external_subdirectories(project_path))
return list(dict.fromkeys(external_subdirectories_data))
def get_all_templates() -> list:
templates_data = set(get_templates())
templates_data.update(get_engine_templates())
return list(templates_data)
def get_all_templates(project_path: pathlib.Path = None) -> list:
templates_data = get_templates()
templates_data.extend(get_engine_templates())
if project_path:
templates_data.extend(get_project_templates(project_path))
return list(dict.fromkeys(templates_data))
def get_all_restricted() -> list:
restricted_data = set(get_restricted())
restricted_data.update(get_engine_restricted())
return list(gems_data)
restricted_data = get_restricted()
restricted_data.extend(get_engine_restricted())
if project_path:
restricted_data.extend(get_project_restricted(project_path))
return list(dict.fromkeys(restricted_data))
# Template functions
def get_project_templates(): # temporary until we have a better way to do this... maybe template_type element
def get_templates_for_project_creation():
project_templates = []
for template in get_all_templates():
if 'Project' in template:
project_templates.append(template)
for template_path in get_all_templates():
template_path = pathlib.Path(template_path)
template_json_path = pathlib.Path(template_path) / 'template.json'
if not validation.valid_o3de_template_json(template_json_path):
continue
project_json_path = template_path / 'Template' / 'project.json'
if validation.valid_o3de_project_json(project_json_path):
project_templates.append(template_path)
return project_templates
def get_gem_templates(): # temporary until we have a better way to do this... maybe template_type element
def get_templates_for_gem_creation():
gem_templates = []
for template in get_all_templates():
if 'Gem' in template:
gem_templates.append(template)
for template_path in get_all_templates():
template_path = pathlib.Path(template_path)
template_json_path = pathlib.Path(template_path) / 'template.json'
if not validation.valid_o3de_template_json(template_json_path):
continue
gem_json_path = template_path / 'Template' / 'gem.json'
if validation.valid_o3de_gem_json(gem_json_path):
gem_templates.append(template_path)
return gem_templates
def get_generic_templates(): # temporary until we have a better way to do this... maybe template_type element
generic_templates = []
for template in get_all_templates():
if 'Project' not in template and 'Gem' not in template:
generic_templates.append(template)
return generic_templates
def get_templates_for_generic_creation(): # temporary until we have a better way to do this... maybe template_type element
def filter_project_and_gem_templates_out(template_path,
templates_for_project_creation = get_templates_for_project_creation(),
templates_for_gem_creation = get_templates_for_gem_creation()):
template_path = pathlib.Path(template_path)
return template_path not in templates_for_project_creation and template_path not in templates_for_gem_creation
return list(filter(filter_project_and_gem_templates_out, get_all_templates()))
def get_all_restricted() -> list:
@@ -523,7 +554,7 @@ def get_template_json_data(template_name: str = None,
return None
def get_restricted_data(restricted_name: str = None,
def get_restricted_json_data(restricted_name: str = None,
restricted_path: str or pathlib.Path = None) -> dict or None:
if not restricted_name and not restricted_path:
logger.error('Must specify either a Restricted name or Restricted Path.')
+251 -245
View File
@@ -13,6 +13,7 @@ import argparse
import json
import hashlib
import logging
import pathlib
import sys
import urllib.parse
@@ -21,219 +22,251 @@ from o3de import manifest, validation
logger = logging.getLogger()
logging.basicConfig()
def print_this_engine(verbose: int) -> None:
def get_project_path(project_path: pathlib.Path, project_name: str) -> pathlib.Path:
if not project_name and not project_path:
logger.error(f'Must either specify a Project path or Project Name.')
return None
if not project_path:
project_path = manifest.get_registered(project_name=project_name)
if not project_path:
logger.error(f'Unable to locate project path from the registered manifest json files:'
f' {str(pathlib.Path("~/.o3de/o3de_manifest.json").expanduser())}, engine.json')
return None
if not project_path.is_dir():
logger.error(f'Project path {project_path} is not a folder.')
return None
return project_path
def print_this_engine(verbose: int) -> int:
engine_data = manifest.get_this_engine()
print(json.dumps(engine_data, indent=4))
result = True
if verbose > 0:
print_engines_data(engine_data)
result = print_manifest_json_data(engine_data, 'engine.json', 'This Engine',
manifest.get_engine_json_data, 'engine_path')
return 0 if result else 1
def print_engines(verbose: int) -> None:
engines_data = manifest.get_engines()
print(json.dumps(engines_data, indent=4))
if verbose > 0:
print_engines_data(engines_data)
return print_manifest_json_data(engines_data, 'engine.json', 'Engines',
manifest.get_engine_json_data, 'engine_path')
return 0
def print_projects(verbose: int) -> None:
def print_projects(verbose: int) -> int:
projects_data = manifest.get_projects()
print(json.dumps(projects_data, indent=4))
if verbose > 0:
print_projects_data(projects_data)
return print_manifest_json_data(projects_data, 'project.json', 'Projects',
manifest.get_project_json_data, 'project_path')
return 0
def print_gems(verbose: int) -> None:
def print_gems(verbose: int) -> int:
gems_data = manifest.get_gems()
print(json.dumps(gems_data, indent=4))
if verbose > 0:
print_gems_data(gems_data)
return print_manifest_json_data(gems_data, 'gem.json', 'Gems',
manifest.get_gem_json_data, 'gem_path')
return 0
def print_templates(verbose: int) -> None:
def print_templates(verbose: int) -> int:
templates_data = manifest.get_templates()
print(json.dumps(templates_data, indent=4))
if verbose > 0:
print_templates_data(templates_data)
return print_manifest_json_data(templates_data, 'template.json', 'Templates',
manifest.get_template_json_data, 'template_path')
return 0
def print_restricted(verbose: int) -> None:
def print_restricted(verbose: int) -> int:
restricted_data = manifest.get_restricted()
print(json.dumps(restricted_data, indent=4))
if verbose > 0:
print_restricted_data(restricted_data)
def print_engine_projects(verbose: int) -> None:
if verbose > 0:
return print_manifest_json_data(restricted_data, 'restricted.json', 'Restricted',
manifest.get_restricted_json_data, 'restricted_path')
return 0
# Engine output methods
def print_engine_projects(verbose: int) -> int:
engine_projects_data = manifest.get_engine_projects()
print(json.dumps(engine_projects_data, indent=4))
if verbose > 0:
print_projects_data(engine_projects_data)
return print_manifest_json_data(engine_projects_data, 'project.json', 'Projects',
manifest.get_project_json_data, 'project_path')
return 0
def print_engine_gems(verbose: int) -> None:
def print_engine_gems(verbose: int) -> int:
engine_gems_data = manifest.get_engine_gems()
print(json.dumps(engine_gems_data, indent=4))
if verbose > 0:
print_gems_data(engine_gems_data)
return print_manifest_json_data(engine_gems_data, 'gem.json', 'Gems',
manifest.get_gem_json_data, 'gem_path')
return 0
def print_engine_templates(verbose: int) -> None:
def print_engine_templates(verbose: int) -> int:
engine_templates_data = manifest.get_engine_templates()
print(json.dumps(engine_templates_data, indent=4))
if verbose > 0:
print_templates_data(engine_templates_data)
return print_manifest_json_data(engine_templates_data, 'template.json', 'Templates',
manifest.get_template_json_data, 'template_path')
return 0
def print_engine_restricted(verbose: int) -> None:
def print_engine_restricted(verbose: int) -> int:
engine_restricted_data = manifest.get_engine_restricted()
print(json.dumps(engine_restricted_data, indent=4))
if verbose > 0:
print_restricted_data(engine_restricted_data)
return print_manifest_json_data(engine_restricted_data, 'restricted.json', 'Restricted',
manifest.get_restricted_json_data, 'restricted_path')
return 0
def print_engine_external_subdirectories(verbose: int) -> None:
def print_engine_external_subdirectories() -> int:
external_subdirs_data = manifest.get_engine_external_subdirectories()
print(json.dumps(external_subdirs_data, indent=4))
return 0
def print_all_projects(verbose: int) -> None:
# Project output methods
def print_project_gems(verbose: int, project_path: pathlib.Path, project_name: str) -> int:
project_path = get_project_path(project_path, project_name)
if not project_path:
return 1
project_gems_data = manifest.get_project_gems(project_path)
print(json.dumps(project_gems_data, indent=4))
if verbose > 0:
return print_manifest_json_data(project_gems_data, 'gem.json', 'Gems',
manifest.get_gem_json_data, 'gem_path')
return 0
def print_project_external_subdirectories(project_path: pathlib.Path, project_name: str) -> int:
project_path = get_project_path(project_path, project_name)
if not project_path:
return 1
external_subdirs_data = manifest.get_project_external_subdirectories(project_path)
print(json.dumps(external_subdirs_data, indent=4))
return 0
def print_project_templates(verbose: int, project_path: pathlib.Path, project_name: str) -> int:
project_path = get_project_path(project_path, project_name)
if not project_path:
return 1
project_templates_data = manifest.get_project_templates(project_path)
print(json.dumps(project_templates_data, indent=4))
if verbose > 0:
return print_manifest_json_data(project_templates_data, 'template.json', 'Templates',
manifest.get_template_json_data, 'template_path')
return 0
def print_project_restricted(verbose: int, project_path: pathlib.Path, project_name: str) -> int:
project_path = get_project_path(project_path, project_name)
if not project_path:
return 1
project_restricted_data = manifest.get_project_restricted(project_path)
print(json.dumps(project_restricted_data, indent=4))
if verbose > 0:
return print_manifest_json_data(project_restricted_data, 'restricted.json', 'Restricted',
manifest.get_restricted_json_data, 'restricted_path')
return 0
def print_all_projects(verbose: int) -> int:
all_projects_data = manifest.get_all_projects()
print(json.dumps(all_projects_data, indent=4))
if verbose > 0:
print_projects_data(all_projects_data)
return print_manifest_json_data(all_projects_data, 'project.json', 'Projects',
manifest.get_project_json_data, 'project_path')
return 0
def print_all_gems(verbose: int) -> None:
def print_all_gems(verbose: int) -> int:
all_gems_data = manifest.get_all_gems()
print(json.dumps(all_gems_data, indent=4))
if verbose > 0:
print_gems_data(all_gems_data)
return print_manifest_json_data(all_gems_data, 'gem.json', 'Gems',
manifest.get_gem_json_data, 'gem_path')
return 0
def print_all_templates(verbose: int) -> None:
def print_all_external_subdirectories() -> int:
all_external_subdirectories_data = manifest.get_all_external_subdirectories()
print(json.dumps(all_external_subdirectories_data, indent=4))
return 0
def print_all_templates(verbose: int) -> int:
all_templates_data = manifest.get_all_templates()
print(json.dumps(all_templates_data, indent=4))
if verbose > 0:
print_templates_data(all_templates_data)
return print_manifest_json_data(all_templates_data, 'template.json', 'Templates',
manifest.get_template_json_data, 'template_path')
return 0
def print_all_restricted(verbose: int) -> None:
def print_all_restricted(verbose: int) -> int:
all_restricted_data = manifest.get_all_restricted()
print(json.dumps(all_restricted_data, indent=4))
if verbose > 0:
print_restricted_data(all_restricted_data)
return print_manifest_json_data(all_restricted_data, 'restricted.json', 'Restricted',
manifest.get_restricted_json_data, 'restricted_path')
return 0
def print_engines_data(engines_data: dict) -> None:
def print_manifest_json_data(uri_json_data: dict, json_filename: str,
print_prefix: str, get_json_func: callable, get_json_data_kw: str) -> int:
print('\n')
print("Engines================================================")
for engine_object in engines_data:
print(f"{print_prefix}================================================")
for manifest_uri in uri_json_data:
# if it's not local it should be in the cache
engine_uri = engine_object['path']
parsed_uri = urllib.parse.urlparse(engine_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(engine_uri.encode())
parsed_uri = urllib.parse.urlparse(manifest_uri)
if parsed_uri.scheme in ['http', 'https', 'ftp', 'ftps']:
repo_sha256 = hashlib.sha256(manifest_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
engine = cache_folder / str(repo_sha256.hexdigest() + '.json')
print(f'{engine_uri}/engine.json cached as:')
manifest_json_path = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
engine_json = pathlib.Path(engine_uri).resolve() / 'engine.json'
manifest_json_path = pathlib.Path(manifest_uri).resolve() / json_filename
with engine_json.open('r') as f:
try:
engine_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{engine_json} failed to load: {str(e)}')
else:
print(engine_json)
print(json.dumps(engine_json_data, indent=4))
print('\n')
json_data = get_json_func(**{get_json_data_kwargs: manifest_json_path})
if json_data:
print(manifest_json_path)
print(json.dumps(json_data, indent=4) + '\n')
return 0
def print_projects_data(projects_data: dict) -> None:
print('\n')
print("Projects================================================")
for project_uri in projects_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(project_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(project_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
project_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
project_json = pathlib.Path(project_uri).resolve() / 'project.json'
with project_json.open('r') as f:
try:
project_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{project_json} failed to load: {str(e)}')
else:
print(project_json)
print(json.dumps(project_json_data, indent=4))
print('\n')
def print_gems_data(gems_data: dict) -> None:
print('\n')
print("Gems================================================")
for gem_uri in gems_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(gem_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(gem_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
gem_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
gem_json = pathlib.Path(gem_uri).resolve() / 'gem.json'
with gem_json.open('r') as f:
try:
gem_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{gem_json} failed to load: {str(e)}')
else:
print(gem_json)
print(json.dumps(gem_json_data, indent=4))
print('\n')
def print_templates_data(templates_data: dict) -> None:
print('\n')
print("Templates================================================")
for template_uri in templates_data:
# if it's not local it should be in the cache
parsed_uri = urllib.parse.urlparse(template_uri)
if parsed_uri.scheme == 'http' or \
parsed_uri.scheme == 'https' or \
parsed_uri.scheme == 'ftp' or \
parsed_uri.scheme == 'ftps':
repo_sha256 = hashlib.sha256(template_uri.encode())
cache_folder = manifest.get_o3de_cache_folder()
template_json = cache_folder / str(repo_sha256.hexdigest() + '.json')
else:
template_json = pathlib.Path(template_uri).resolve() / 'template.json'
with template_json.open('r') as f:
try:
template_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{template_json} failed to load: {str(e)}')
else:
print(template_json)
print(json.dumps(template_json_data, indent=4))
print('\n')
def print_repos_data(repos_data: dict) -> None:
def print_repos_data(repos_data: dict) -> int:
print('\n')
print("Repos================================================")
cache_folder = manifest.get_o3de_cache_folder()
@@ -251,29 +284,16 @@ def print_repos_data(repos_data: dict) -> None:
print(cache_file)
print(json.dumps(repo_json_data, indent=4))
print('\n')
def print_restricted_data(restricted_data: dict) -> None:
print('\n')
print("Restricted================================================")
for restricted_path in restricted_data:
restricted_json = pathlib.Path(restricted_path).resolve() / 'restricted.json'
with restricted_json.open('r') as f:
try:
restricted_json_data = json.load(f)
except json.JSONDecodeError as e:
logger.warn(f'{restricted_json} failed to load: {str(e)}')
else:
print(restricted_json)
print(json.dumps(restricted_json_data, indent=4))
print('\n')
return 0
def register_show_repos(verbose: int) -> None:
repos_data = get_repos()
repos_data = manifest.get_repos()
print(json.dumps(repos_data, indent=4))
if verbose > 0:
print_repos_data(repos_data)
return print_repos_data(repos_data) == 0
return 0
def register_show(verbose: int) -> None:
@@ -281,13 +301,15 @@ def register_show(verbose: int) -> None:
print(f"{manifest.get_o3de_manifest()}:")
print(json.dumps(json_data, indent=4))
result = True
if verbose > 0:
print_engines_data(manifest.get_engines())
print_projects_data(manifest.get_all_projects())
print_gems_data(manifest.get_gems())
print_templates_data(manifest.get_all_templates())
print_restricted_data(manifest.get_all_restricted())
print_repos_data(manifest.get_repos())
result = print_manifest_json_data(manifest.get_engines()) == 0 and result
result = print_manifest_json_data(manifest.get_all_projects()) == 0 and result
result = print_manifest_json_data(manifest.get_gems()) == 0 and result
result = print_manifest_json_data(manifest.get_all_templates()) == 0 and result
result = print_manifest_json_data(manifest.get_all_restricted()) == 0 and result
result = print_repos_data(manifest.get_repos()) == 0 and result
return 0 if result else 1
def _run_register_show(args: argparse) -> int:
@@ -295,75 +317,53 @@ def _run_register_show(args: argparse) -> int:
manifest.override_home_folder = args.override_home_folder
if args.this_engine:
print_this_engine(args.verbose)
return 0
return print_this_engine(args.verbose)
elif args.engines:
print_engines(args.verbose)
return 0
return print_engines(args.verbose)
elif args.projects:
print_projects(args.verbose)
return 0
return print_projects(args.verbose)
elif args.gems:
print_gems(args.verbose)
return 0
return print_gems(args.verbose)
elif args.templates:
print_templates(args.verbose)
return 0
return print_templates(args.verbose)
elif args.repos:
register_show_repos(args.verbose)
return 0
return register_show_repos(args.verbose)
elif args.restricted:
print_restricted(args.verbose)
return 0
return print_restricted(args.verbose)
elif args.engine_projects:
print_engine_projects(args.verbose)
return 0
return print_engine_projects(args.verbose)
elif args.engine_gems:
print_engine_gems(args.verbose)
return 0
elif args.engine_templates:
print_engine_templates(args.verbose)
return 0
elif args.engine_restricted:
print_engine_restricted(args.verbose)
return 0
return print_engine_gems(args.verbose)
elif args.engine_external_subdirectories:
print_engine_external_subdirectories(args.verbose)
return 0
return print_engine_external_subdirectories()
elif args.engine_templates:
return print_engine_templates(args.verbose)
elif args.engine_restricted:
return print_engine_restricted(args.verbose)
elif args.project_gems:
return print_project_gems(args.verbose, args.project_path, args.project_name)
elif args.project_external_subdirectories:
return print_project_external_subdirectories(args.project_path, args.project_name)
elif args.project_templates:
return print_project_templates(args.verbose, args.project_path, args.project_name)
elif args.project_restricted:
return print_project_restricted(args.verbose, args.project_path, args.project_name)
elif args.all_projects:
print_all_projects(args.verbose)
return 0
return print_all_projects(args.verbose)
elif args.all_gems:
print_all_gems(args.verbose)
return 0
return print_all_gems(args.verbose)
elif args.all_external_subdirectories:
return print_all_external_subdirectories()
elif args.all_templates:
print_all_templates(args.verbose)
return 0
return print_all_templates(args.verbose)
elif args.all_restricted:
print_all_restricted(args.verbose)
return 0
return print_all_restricted(args.verbose)
elif args.downloadables:
print_downloadables(args.verbose)
return 0
if args.downloadable_engines:
print_downloadable_engines(args.verbose)
return 0
elif args.downloadable_projects:
print_downloadable_projects(args.verbose)
return 0
elif args.downloadable_gems:
print_downloadable_gems(args.verbose)
return 0
elif args.downloadable_templates:
print_downloadable_templates(args.verbose)
return 0
else:
register_show(args.verbose)
return 0
return register_show(args.verbose)
def add_parser_args(parser):
@@ -376,78 +376,84 @@ def add_parser_args(parser):
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('-te', '--this-engine', action='store_true', required=False,
default=False,
help='Just the local engines.')
help='Output the current engine path.')
group.add_argument('-e', '--engines', action='store_true', required=False,
default=False,
help='Just the local engines.')
help='Output the engines registered in the global ~/.o3de/o3de_manifest.json.')
group.add_argument('-p', '--projects', action='store_true', required=False,
default=False,
help='Just the local projects.')
help='Output the projects registered in the global ~/.o3de/o3de_manifest.json.')
group.add_argument('-g', '--gems', action='store_true', required=False,
default=False,
help='Just the local gems.')
help='Output the gems registered in the global ~/.o3de/o3de_manifest.json.')
group.add_argument('-t', '--templates', action='store_true', required=False,
default=False,
help='Just the local templates.')
help='Output the templates registered in the global ~/.o3de/o3de_manifest.json.')
group.add_argument('-r', '--repos', action='store_true', required=False,
default=False,
help='Just the local repos. Ignores repos.')
help='Output the repos registered in the global ~/.o3de/o3de_manifest.json. Ignores repos.')
group.add_argument('-rs', '--restricted', action='store_true', required=False,
default=False,
help='The local restricted folders.')
help='Output the restricted directories registered in the global ~/.o3de/o3de_manifest.json.')
group.add_argument('-ep', '--engine-projects', action='store_true', required=False,
default=False,
help='Just the local projects. Ignores repos.')
help='Output the projects registered in the current engine engine.json. Ignores repos.')
group.add_argument('-eg', '--engine-gems', action='store_true', required=False,
default=False,
help='Just the local gems. Ignores repos')
help='Output the gems registered in the current engine engine.json. Ignores repos')
group.add_argument('-et', '--engine-templates', action='store_true', required=False,
default=False,
help='Just the local templates. Ignores repos.')
help='Output the templates registered in the current engine engine.json. Ignores repos.')
group.add_argument('-ers', '--engine-restricted', action='store_true', required=False,
default=False,
help='The restricted folders.')
group.add_argument('-x', '--engine-external-subdirectories', action='store_true', required=False,
help='Output the restricted directories registered in the current engine engine.json.')
group.add_argument('-ees', '--engine-external-subdirectories', action='store_true', required=False,
default=False,
help='The external subdirectories.')
help='Output the external subdirectories registered in the current engine engine.json.')
group.add_argument('-pg', '--project-gems', action='store_true',
default=False,
help='Returns the gems registered with the project.json.')
group.add_argument('-pt', '--project-templates', action='store_true',
default=False,
help='Returns the templates registered with the project.json.')
group.add_argument('-prs', '--project-restricted', action='store_true',
default=False,
help='Returns the restricted directories registered with the project.json.')
group.add_argument('-pes', '--project-external-subdirectories', action='store_true',
default=False,
help='Returns the external subdirectories register with the project.json.')
group.add_argument('-ap', '--all-projects', action='store_true', required=False,
default=False,
help='Just the local projects. Ignores repos.')
help='Output all projects registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.')
group.add_argument('-ag', '--all-gems', action='store_true', required=False,
default=False,
help='Just the local gems. Ignores repos')
help='Output all gems registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos')
group.add_argument('-at', '--all-templates', action='store_true', required=False,
default=False,
help='Just the local templates. Ignores repos.')
group.add_argument('-ars', '--all-restricted', action='store_true', required=False,
help='Output all templates registered in the ~/.o3de/o3de_manifest.json and the current engine.json. Ignores repos.')
group.add_argument('-ares', '--all-restricted', action='store_true', required=False,
default=False,
help='The restricted folders.')
group.add_argument('-d', '--downloadables', action='store_true', required=False,
help='Output all restricted directory registered in the ~/.o3de/o3de_manifest.json and the current engine.json.')
group.add_argument('-aes', '--all-external-subdirectories', action='store_true',
default=False,
help='Combine all repos into a single list of resources.')
group.add_argument('-de', '--downloadable-engines', action='store_true', required=False,
default=False,
help='Combine all repos engines into a single list of resources.')
group.add_argument('-dp', '--downloadable-projects', action='store_true', required=False,
default=False,
help='Combine all repos projects into a single list of resources.')
group.add_argument('-dg', '--downloadable-gems', action='store_true', required=False,
default=False,
help='Combine all repos gems into a single list of resources.')
group.add_argument('-dt', '--downloadable-templates', action='store_true', required=False,
default=False,
help='Combine all repos templates into a single list of resources.')
help='Output all external subdirectories registered in the ~/.o3de/o3de_manifest.json and the current engine.json.')
parser.add_argument('-v', '--verbose', action='count', required=False,
default=0,
help='How verbose do you want the output to be.')
default=0,
help='How verbose do you want the output to be.')
project_group = parser.add_mutually_exclusive_group(required=False)
project_group.add_argument('-pp', '--project-path', type=pathlib.Path,
help='The path to a project.')
project_group.add_argument('-pn', '--project-name', type=str,
help='The name of a project.')
parser.add_argument('-ohf', '--override-home-folder', type=str, required=False,
help='By default the home folder is the user folder, override it to this folder.')
help='By default the home folder is the user folder, override it to this folder.')
parser.set_defaults(func=_run_register_show)
+7
View File
@@ -34,3 +34,10 @@ ly_add_pytest(
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
ly_add_pytest(
NAME o3de_manifest
PATH ${CMAKE_CURRENT_LIST_DIR}/unit_test_manifest.py
TEST_SUITE smoke
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
)
+111
View File
@@ -0,0 +1,111 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import argparse
import json
import logging
import pytest
import pathlib
from unittest.mock import patch
from o3de import manifest
@pytest.mark.parametrize("valid_project_json_paths, valid_gem_json_paths", [
pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject/Template/project.json')],
[pathlib.Path('D:/o3de/Templates/DefaultGem/Template/gem.json')])
])
class TestGetTemplatesForCreation:
@staticmethod
def get_templates() -> list:
return []
@staticmethod
def get_project_templates() -> list:
return []
@staticmethod
def get_engine_templates() -> list:
return [pathlib.Path('D:/o3de/Templates/DefaultProject'), pathlib.Path('D:/o3de/Templates/DefaultGem')]
@pytest.mark.parametrize("expected_template_paths", [
pytest.param([])
]
)
def test_get_templates_for_generic_creation(self, valid_project_json_paths, valid_gem_json_paths,
expected_template_paths):
def validate_project_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_project_json_paths
def validate_gem_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_gem_json_paths
with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \
patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates)\
as get_project_templates_patch, \
patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates)\
as get_engine_templates_patch, \
patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json,\
patch('o3de.validation.valid_o3de_project_json', side_effect=validate_project_json) as validate_project_json,\
patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json:
templates = manifest.get_templates_for_generic_creation()
assert templates == expected_template_paths
@pytest.mark.parametrize("expected_template_paths", [
pytest.param([pathlib.Path('D:/o3de/Templates/DefaultProject')])
]
)
def test_get_templates_for_gem_creation(self, valid_project_json_paths, valid_gem_json_paths,
expected_template_paths):
def validate_project_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_project_json_paths
def validate_gem_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_gem_json_paths
with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \
patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \
as get_project_templates_patch, \
patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \
as get_engine_templates_patch, \
patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \
patch('o3de.validation.valid_o3de_project_json',
side_effect=validate_project_json) as validate_project_json, \
patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json:
templates = manifest.get_templates_for_project_creation()
assert templates == expected_template_paths
@pytest.mark.parametrize("expected_template_paths", [
pytest.param([pathlib.Path('D:/o3de/Templates/DefaultGem')])
]
)
def test_get_templates_for_project_creation(self, valid_project_json_paths, valid_gem_json_paths,
expected_template_paths):
def validate_project_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_project_json_paths
def validate_gem_json(template_path) -> bool:
return pathlib.Path(template_path) in valid_gem_json_paths
with patch('o3de.manifest.get_templates', side_effect=self.get_templates) as get_templates_patch, \
patch('o3de.manifest.get_project_templates', side_effect=self.get_project_templates) \
as get_project_templates_patch, \
patch('o3de.manifest.get_engine_templates', side_effect=self.get_engine_templates) \
as get_engine_templates_patch, \
patch('o3de.validation.valid_o3de_template_json', return_value=True) as validate_template_json, \
patch('o3de.validation.valid_o3de_project_json',
side_effect=validate_project_json) as validate_project_json, \
patch('o3de.validation.valid_o3de_gem_json', side_effect=validate_gem_json) as validate_gem_json:
templates = manifest.get_templates_for_gem_creation()
assert templates == expected_template_paths
+1 -1
View File
@@ -11,7 +11,7 @@
import pytest
from . import utils
from o3de import utils
@pytest.mark.parametrize(
"value, expected_result", [