Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,78 @@
--
--
-- 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.
--
function ScriptTrace(txt)
Debug.Log(txt)
end
function ScriptExpectTrue(condition, msg)
if (not condition) then
ScriptTrace(msg)
end
end
-- This example shows how to implement a handler for a Script Event that requires an address
-- in order for a handler to be invoked
luaScriptEventWithId = {
-- This method will be broadcast, but only handlers connected to the matching address
-- as the one specified in the event will invoke it
MethodWithId0 = function(self, param1, param2)
ScriptTrace("Handler: " .. tostring(param1) .. " " .. tostring(param2))
ScriptExpectTrue(typeid(param1) == typeid(0), "Type of param1 must be "..tostring(typeid(0)))
ScriptExpectTrue(typeid(param2) == typeid(EntityId()), "Type of param2 must be "..tostring(typeid(EntityId())))
ScriptExpectTrue(param1 == 1, "The first parameter must be 1")
ScriptExpectTrue(param2 == EntityId(12345), "The received entity Id must match the one sent")
ScriptTrace("MethodWithId0 handled")
return true
end,
MethodWithId1 = function(self)
ScriptTrace("MethodWithId1 handled")
end
}
-- "Script_Event" will be the name of the callable Script Event, it will require the address type to be a string.
local scriptEventDefinition = ScriptEvent("Script_Event", typeid("")) -- Event address is of string type
-- Will define some methods that handlers may implement
local method0 = scriptEventDefinition:AddMethod("MethodWithId0", typeid(false)) -- Return value is Boolean
method0:AddParameter("Param0", typeid(0))
method0:AddParameter("Param1", typeid(EntityId()))
-- NOTE: Type's are specified using the typeid keyword with a VALUE of the type you wish (for example, typeid("EntityId")
-- will produce the type id for a string, and not the type of EntityId)
scriptEventDefinition:AddMethod("MethodWithId1") -- No return, no parameters
-- Once the Script Event is defined, call Register to enable it, typically this should be done within OnActivate
scriptEventDefinition:Register()
-- At this point, the Script Event is usable, so we will connect a handler to it, this will install luaScriptEventWithId as the Handler
-- which will provide implementations to the methods we defined. Notice that we are connecting with the string "ScriptEventAddress"
-- as the address for this event. Any methods sent to a different address would not be handled by this handler we are connecting.
scriptEventHandler = Script_Event.Connect(luaScriptEventWithId, "ScriptEventAddress")
-- Now we will invoke the event and we will specify "ScriptEventAddress" as the address, this means the handler we previously
-- connected will be able to handle this event.
local returnValue = Script_Event.Event.MethodWithId0("ScriptEventAddress", 1, EntityId(12345))
-- We know that "Method0" should return true, we verify that it is.
ScriptExpectTrue(returnValue, "Method0's return value must be true")
-- Finally we send "MethodWithdId1" which does not require any parameters, but still needs the address to be provided.
Script_Event.Event.MethodWithId1("ScriptEventAddress")
@@ -0,0 +1,75 @@
--
--
-- 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.
--
function ScriptTrace(txt)
Debug.Log(txt)
end
function ScriptExpectTrue(condition, msg)
if (not condition) then
ScriptTrace(msg)
end
end
-- This example shows how to implement a handler for a Script Event Broadcast
-- Broadcast Script Events do not specify an address type and so may be handled
-- by connecting to the Script Event.
luaScriptEventBroadcast = {
-- This method will be called as a result of a Broadcast call on the Script Event.
BroadcastMethod0 = function(self, param1, param2)
ScriptTrace("Handler: " .. tostring(param1) .. " " .. tostring(param2))
ScriptExpectTrue(typeid(param1) == typeid(0), "Type of param1 must be "..tostring(typeid(0)))
ScriptExpectTrue(typeid(param2) == typeid(EntityId()), "Type of param2 must be "..tostring(typeid(EntityId())))
ScriptExpectTrue(param1 == 2, "The first parameter must be 2")
ScriptExpectTrue(param2 == EntityId(23456), "The received entity Id must match the one sent")
ScriptTrace("BroadcastMethod0 Called")
return true
end,
BroadcastMethod1 = function(self)
ScriptTrace("BroadcastMethod1 Called")
end
}
local scriptEventDefinition = ScriptEvent("Script_Broadcast") -- Script_Broadcast will be the name of the callable Script Event
-- Define methods for Script_Broadcast
local method0 = scriptEventDefinition:AddMethod("BroadcastMethod0", typeid(false)) -- Adding a method expects a method name and an optional return type.
method0:AddParameter("Param0", typeid(0))
method0:AddParameter("Param1", typeid(EntityId()))
-- NOTE: Type's are specified using the typeid keyword with a VALUE of the type you wish (for example, typeid("EntityId")
-- will produce the type id for a string, and not the type of EntityId)
scriptEventDefinition:AddMethod("BroadcastMethod1")
-- Once the Script Event is defined, call Register to enable it, typically this should be done within OnActivate
scriptEventDefinition:Register()
-- At this point, the Script Event is usable, so we will connect a handler to it, this will install luaScriptEventBroadcast as the Handler
-- which will provide implementations to the methods we defined.
scriptEventHandler = Script_Broadcast.Connect(luaScriptEventBroadcast)
-- In order to test the event, we will Broadcast "BroadcastMethod0" which as defined will return a Boolean value and expects two parameters
local returnValue = Script_Broadcast.Broadcast.BroadcastMethod0(2, EntityId(23456))
-- We know that BroadcastMethod0 should return true, so we will verify the result.
ScriptExpectTrue(returnValue, "BroadcastMethod0's return value must be true")
-- Broadcast an event without a return or parameters, the BroadcastMethod1 will be invoked
Script_Broadcast.Broadcast.BroadcastMethod1()
+12
View File
@@ -0,0 +1,12 @@
#
# 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.
#
add_subdirectory(Code)
@@ -0,0 +1,44 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace ScriptEventsBuilder
{
class BuilderSystemComponent
: public AZ::Component
{
public:
AZ_COMPONENT(BuilderSystemComponent, "{6CE4EF5D-5A18-4E25-A676-501644676B58}");
BuilderSystemComponent();
~BuilderSystemComponent() override;
static void Reflect(AZ::ReflectContext* context);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
private:
BuilderSystemComponent(const BuilderSystemComponent&) = delete;
};
}
@@ -0,0 +1,83 @@
/*
* 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.
*
*/
#include <precompiled.h>
#include <Builder/ScriptEventsBuilderComponent.h>
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogBus.h>
#include <ScriptEvents/ScriptEventsAsset.h>
namespace ScriptEventsBuilder
{
void ScriptEventsBuilderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ScriptEventsBuilderService", 0x049e945c));
}
void ScriptEventsBuilderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
AZ_UNUSED(required);
}
void ScriptEventsBuilderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("AssetCatalogService", 0xc68ffc57));
}
void ScriptEventsBuilderComponent::Activate()
{
// Register ScriptEvents Builder
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
builderDescriptor.m_name = "Script Events Builder";
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern("*.scriptevents", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
builderDescriptor.m_busId = ScriptEventsBuilder::Worker::GetUUID();
builderDescriptor.m_createJobFunction = AZStd::bind(&Worker::CreateJobs, &m_scriptEventsBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
builderDescriptor.m_processJobFunction = AZStd::bind(&Worker::ProcessJob, &m_scriptEventsBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
// changing the version number invalidates all assets and will rebuild everything.
builderDescriptor.m_version = m_scriptEventsBuilder.GetVersionNumber();
// changing the analysis fingerprint just invalidates analysis (ie, not the assets themselves)
// which will cause the "CreateJobs" function to be called, for each asset, even if the
// source file has not changed, but won't actually do the jobs unless the source file has changed
// or the fingerprint of the individual job is different.
builderDescriptor.m_analysisFingerprint = m_scriptEventsBuilder.GetFingerprintString();
m_scriptEventsBuilder.BusConnect(builderDescriptor.m_busId);
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, builderDescriptor);
AzToolsFramework::ToolsAssetSystemBus::Broadcast(&AzToolsFramework::ToolsAssetSystemRequests::RegisterSourceAssetType, azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), ScriptEvents::ScriptEventsAsset::GetFileFilter());
m_scriptEventsBuilder.Activate();
}
void ScriptEventsBuilderComponent::Deactivate()
{
// Finish all queued work
AZ::Data::AssetBus::ExecuteQueuedEvents();
AzToolsFramework::ToolsAssetSystemBus::Broadcast(&AzToolsFramework::ToolsAssetSystemRequests::UnregisterSourceAssetType, azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
m_scriptEventsBuilder.Deactivate();
m_scriptEventsBuilder.BusDisconnect();
}
void ScriptEventsBuilderComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ScriptEventsBuilderComponent, AZ::Component>()
->Version(0)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }));
;
}
}
}
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
#pragma once
#include "ScriptEventsBuilderWorker.h"
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/Component/Component.h>
namespace ScriptEventsBuilder
{
//! ScriptEventsBuilder is responsible for turning editor ScriptEvents Assets into runtime script canvas assets
class ScriptEventsBuilderComponent
: public AZ::Component
{
public:
AZ_COMPONENT(ScriptEventsBuilderComponent, "{A402F019-0DD4-4CFF-B8A0-A90F818021E4}")
static void Reflect(AZ::ReflectContext* context);
ScriptEventsBuilderComponent() = default;
~ScriptEventsBuilderComponent() override = default;
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
ScriptEventsBuilderComponent(const ScriptEventsBuilderComponent&) = delete;
Worker m_scriptEventsBuilder;
};
}
@@ -0,0 +1,258 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AzCore/Asset/AssetDataStream.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/IOUtils.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <Builder/ScriptEventsBuilderWorker.h>
#include <Editor/ScriptEventsSystemEditorComponent.h>
#include <ScriptEvents/ScriptEventsAsset.h>
namespace ScriptEventsBuilder
{
static const char* s_scriptEventsBuilder = "ScriptEventsBuilder";
Worker::Worker()
{
}
Worker::~Worker()
{
Deactivate();
}
int Worker::GetVersionNumber() const
{
return 1;
}
const char* Worker::GetFingerprintString() const
{
if (m_fingerprintString.empty())
{
// compute it the first time
const AZStd::string runtimeAssetTypeId = azrtti_typeid<ScriptEvents::ScriptEventsAsset>().ToString<AZStd::string>();
m_fingerprintString = AZStd::string::format("%i%s", GetVersionNumber(), runtimeAssetTypeId.c_str());
}
return m_fingerprintString.c_str();
}
void Worker::Activate()
{
}
void Worker::Deactivate()
{
}
void Worker::ShutDown()
{
m_isShuttingDown = true;
}
void Worker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const
{
AZStd::string fullPath;
AzFramework::StringFunc::Path::ConstructFull(request.m_watchFolder.data(), request.m_sourceFile.data(), fullPath, false);
AzFramework::StringFunc::Path::Normalize(fullPath);
AZ_TracePrintf(s_scriptEventsBuilder, "CreateJobs for script events\"%s\"\n", fullPath.data());
AZ::Data::AssetHandler* editorAssetHandler = AZ::Data::AssetManager::Instance().GetHandler(azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
if (!editorAssetHandler)
{
AZ_Error(s_scriptEventsBuilder, false, R"(CreateJobs for %s failed because the ScriptEvents Editor Asset handler is missing.)", fullPath.data());
}
AZStd::shared_ptr<AZ::Data::AssetDataStream> assetDataStream = AZStd::make_shared<AZ::Data::AssetDataStream>();
// Read the asset into a memory buffer, then hand ownership of the buffer to assetDataStream
{
AZ::IO::FileIOStream stream(fullPath.c_str(), AZ::IO::OpenMode::ModeRead);
if (!AZ::IO::RetryOpenStream(stream))
{
AZ_Warning(s_scriptEventsBuilder, false, "CreateJobs for \"%s\" failed because the source file could not be opened.", fullPath.data());
return;
}
AZStd::vector<AZ::u8> fileBuffer(stream.GetLength());
size_t bytesRead = stream.Read(fileBuffer.size(), fileBuffer.data());
if (bytesRead != stream.GetLength())
{
AZ_Warning(s_scriptEventsBuilder, false, "CreateJobs for \"%s\" failed because the source file could not be read.", fullPath.data());
return;
}
assetDataStream->Open(AZStd::move(fileBuffer));
}
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> asset;
asset.Create(AZ::Data::AssetId(AZ::Uuid::CreateRandom()));
if (editorAssetHandler->LoadAssetDataFromStream(asset, assetDataStream, nullptr) != AZ::Data::AssetHandler::LoadResult::LoadComplete)
{
AZ_Warning(s_scriptEventsBuilder, false, "CreateJobs for \"%s\" failed because the asset data could not be loaded from the file", fullPath.data());
return;
}
// Flush asset database events to ensure no asset references are held by closures queued on Ebuses.
AZ::Data::AssetManager::Instance().DispatchEvents();
for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms)
{
AssetBuilderSDK::JobDescriptor jobDescriptor;
jobDescriptor.m_priority = 2;
jobDescriptor.m_critical = true;
jobDescriptor.m_jobKey = "Script Events";
jobDescriptor.SetPlatformIdentifier(info.m_identifier.data());
jobDescriptor.m_additionalFingerprintInfo = GetFingerprintString();
response.m_createJobOutputs.push_back(jobDescriptor);
}
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
}
void Worker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const
{
// A runtime script events component is generated, which creates a .scriptevents_compiled file
AZStd::string fullPath;
AZStd::string fileNameOnly;
AzFramework::StringFunc::Path::GetFullFileName(request.m_sourceFile.c_str(), fileNameOnly);
fullPath = request.m_fullPath.c_str();
AzFramework::StringFunc::Path::Normalize(fullPath);
AZ_TracePrintf(s_scriptEventsBuilder, "Processing script events \"%s\".\n", fullPath.c_str());
auto editorAssetHandler = azrtti_cast<ScriptEventsEditor::ScriptEventAssetHandler*>(
AZ::Data::AssetManager::Instance().GetHandler(azrtti_typeid<ScriptEvents::ScriptEventsAsset>()));
if (!editorAssetHandler)
{
AZ_Error(s_scriptEventsBuilder, false, R"(Exporting of .ScriptEvents for "%s" file failed as no editor asset handler was registered for scrit events. The ScriptEvents Gem might not be enabled.)", fullPath.data());
return;
}
AZStd::shared_ptr<AZ::Data::AssetDataStream> assetDataStream = AZStd::make_shared<AZ::Data::AssetDataStream>();
// Read the asset into a memory buffer, then hand ownership of the buffer to assetDataStream
{
AZ::IO::FileIOStream stream(fullPath.c_str(), AZ::IO::OpenMode::ModeRead);
if (!stream.IsOpen())
{
AZ_Warning(s_scriptEventsBuilder, false, "Exporting of .ScriptEvents for \"%s\" failed because the source file could not be opened.", fullPath.c_str());
return;
}
AZStd::vector<AZ::u8> fileBuffer(stream.GetLength());
size_t bytesRead = stream.Read(fileBuffer.size(), fileBuffer.data());
if (bytesRead != stream.GetLength())
{
AZ_Warning(s_scriptEventsBuilder, false, "Exporting of .ScriptEvents for \"%s\" failed because the source file could not be read.", fullPath.c_str());
return;
}
assetDataStream->Open(AZStd::move(fileBuffer));
}
AZ::SerializeContext* context{};
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
AZ_TracePrintf(s_scriptEventsBuilder, "Script Events Asset preload\n");
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> asset;
asset.Create(AZ::Data::AssetId(AZ::Uuid::CreateRandom()));
if (editorAssetHandler->LoadAssetData(asset, assetDataStream, nullptr) != AZ::Data::AssetHandler::LoadResult::LoadComplete)
{
AZ_Error(s_scriptEventsBuilder, false, R"(Loading of ScriptEvents asset for source file "%s" has failed)", fullPath.data());
return;
}
AZ_TracePrintf(s_scriptEventsBuilder, "Script Events Asset loaded successfully\n");
// Flush asset manager events to ensure no asset references are held by closures queued on Ebuses.
AZ::Data::AssetManager::Instance().DispatchEvents();
AZStd::string runtimeScriptEventsOutputPath;
AzFramework::StringFunc::Path::Join(request.m_tempDirPath.c_str(), fileNameOnly.c_str(), runtimeScriptEventsOutputPath, true, true);
ScriptEvents::ScriptEvent definition = asset.Get()->m_definition;
definition.Flatten();
// Populate the runtime Asset
AZStd::vector<AZ::u8> byteBuffer;
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> productionAsset;
productionAsset.Create(AZ::Uuid::CreateRandom());
productionAsset.Get()->m_definition = AZStd::move(definition);
editorAssetHandler->SetSaveAsBinary(true);
AZ_TracePrintf(s_scriptEventsBuilder, "Script Events Asset presave to object stream for %s\n", fullPath.c_str());
bool productionAssetSaved = editorAssetHandler->SaveAssetData(productionAsset, &byteStream);
if (!productionAssetSaved)
{
AZ_Error(s_scriptEventsBuilder, productionAssetSaved, "Failed to save runtime Script Events to object stream");
return;
}
AZ_TracePrintf(s_scriptEventsBuilder, "Script Events Asset has been saved to the object stream successfully\n");
// TODO: make this binary
AZ::IO::FileIOStream outFileStream(runtimeScriptEventsOutputPath.data(), AZ::IO::OpenMode::ModeWrite);
if (!outFileStream.IsOpen())
{
AZ_Error(s_scriptEventsBuilder, false, "Failed to open output file %s", runtimeScriptEventsOutputPath.data());
return;
}
productionAssetSaved = outFileStream.Write(byteBuffer.size(), byteBuffer.data()) == byteBuffer.size() && productionAssetSaved;
if (!productionAssetSaved)
{
AZ_Error(s_scriptEventsBuilder, productionAssetSaved, "Unable to save runtime Script Events file %s", runtimeScriptEventsOutputPath.data());
return;
}
// ScriptEvents Editor Asset Copy job
// The SubID is zero as this represents the main asset
AssetBuilderSDK::JobProduct jobProduct;
jobProduct.m_productFileName = runtimeScriptEventsOutputPath;
jobProduct.m_productAssetType = azrtti_typeid<ScriptEvents::ScriptEventsAsset>();
jobProduct.m_productSubID = 0;
jobProduct.m_dependenciesHandled = true; // This builder has no product dependencies.
response.m_outputProducts.push_back(AZStd::move(jobProduct));
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
AZ_TracePrintf(s_scriptEventsBuilder, "Finished processing Script Events %s\n", fullPath.c_str());
}
AZ::Uuid Worker::GetUUID()
{
return AZ::Uuid::CreateString("{CD64F85A-0147-45EF-B02A-9828E25D99EB}");
}
}
@@ -0,0 +1,58 @@
/*
* 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.
*
*/
#pragma once
#include <AssetBuilderSDK/AssetBuilderBusses.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Asset/AssetCommon.h>
namespace ScriptEventsEditor { class ScriptEventAssetHandler; }
namespace ScriptEventsBuilder
{
class Worker
: public AssetBuilderSDK::AssetBuilderCommandBus::Handler
{
public:
Worker();
~Worker();
int GetVersionNumber() const;
const char* GetFingerprintString() const;
//! Asset Builder Callback Functions
void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const;
void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const;
//////////////////////////////////////////////////////////////////////////
//!AssetBuilderSDK::AssetBuilderCommandBus interface
void ShutDown() override;
//////////////////////////////////////////////////////////////////////////
void Activate();
void Deactivate();
static AZ::Uuid GetUUID();
private:
Worker(const Worker&) = delete;
bool m_isShuttingDown = false;
// cached on first time query
mutable AZStd::string m_fingerprintString;
};
}
+92
View File
@@ -0,0 +1,92 @@
#
# 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.
#
ly_add_target(
NAME ScriptEvents.Static STATIC
NAMESPACE Gem
FILES_CMAKE
scriptevents_common_files.cmake
INCLUDE_DIRECTORIES
PUBLIC
Include
PRIVATE
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
)
ly_add_target(
NAME ScriptEvents ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
NAMESPACE Gem
OUTPUT_NAME Gem.ScriptEvents.32d8ba21703e4bbbb08487366e48dd69.v0.1.0
FILES_CMAKE
scriptevents_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
Gem::ScriptEvents.Static
)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME ScriptEvents.Editor MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.ScriptEvents.Editor.32d8ba21703e4bbbb08487366e48dd69.v0.1.0
FILES_CMAKE
scriptevents_editor_files.cmake
scriptevents_editor_builder_files.cmake
COMPILE_DEFINITIONS
PRIVATE
SCRIPTEVENTS_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
.
Source
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AssetBuilderSDK
Gem::ScriptEvents.Static
)
endif()
################################################################################
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME ScriptEvents.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE Gem
FILES_CMAKE
scriptevents_files.cmake
scriptevents_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
Include/ScriptEvents
Source
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::AzFramework
Gem::ScriptEvents.Static
)
ly_add_googletest(
NAME Gem::ScriptEvents.Tests
)
endif()
@@ -0,0 +1,97 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventReferencesComponent.h"
namespace ScriptEvents
{
namespace Components
{
void ScriptEventReferencesComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
// The Script Event References component is no longer necessary, as all Script Event assets
// will be properly loaded as needed.
serializeContext->ClassDeprecate("ScriptEventReferencesComponent", "{D0F440AC-32D4-49EC-8B93-860B188266A6}");
}
}
void ScriptEventReferencesComponent::Activate()
{
for (auto& scriptEventReferences : m_scriptEventAssets)
{
const auto& asset = scriptEventReferences.GetAsset();
if (asset)
{
if (!AZ::Data::AssetBus::MultiHandler::BusIsConnectedId(asset.GetId()))
{
AZ::Data::AssetBus::MultiHandler::BusConnect(asset.GetId());
}
// Load the asset if it's not ready
if (!asset.IsReady())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, asset.GetId());
if (assetInfo.m_assetId.IsValid())
{
AZ::Data::AssetManager::Instance().GetAsset(asset.GetId(), azrtti_typeid<ScriptEventsAsset>(), AZ::Data::AssetLoadBehavior::Default)
.BlockUntilLoadComplete();
}
}
}
else
{
AZ_Warning("Script Events", false, "ScriptEventReferencesComponent could not find Script Event asset: %s", scriptEventReferences.GetDefinition() ? scriptEventReferences.GetDefinition()->GetName().c_str() : scriptEventReferences.GetAsset().GetId().ToString<AZStd::string>().c_str());
}
}
}
void ScriptEventReferencesComponent::Deactivate()
{
for (auto& scriptEventReferences : m_scriptEventAssets)
{
const auto& asset = scriptEventReferences.GetAsset();
if (asset)
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
}
}
}
void ScriptEventReferencesComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ScriptEventReference", 0x3df92d40));
}
void ScriptEventReferencesComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ScriptEventReference", 0x3df92d40));
}
void ScriptEventReferencesComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("LuaScriptService", 0x21d76c4b));
}
void ScriptEventReferencesComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (ScriptEventsAsset* scriptEventAsset = asset.GetAs<ScriptEventsAsset>())
{
scriptEventAsset->m_definition.RegisterInternal();
}
}
}
}
@@ -0,0 +1,48 @@
/*
* 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.
*
*/
#pragma once
#include <ScriptEvents/ScriptEventsAssetRef.h>
#include <AzCore/Asset/AssetCommon.h>
namespace ScriptEvents
{
namespace Components
{
class ScriptEventReferencesComponent
: public AZ::Component
, private AZ::Data::AssetBus::MultiHandler
{
public:
AZ_COMPONENT(ScriptEventReferencesComponent, "{D0F440AC-32D4-49EC-8B93-860B188266A6}", AZ::Component);
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Init() override {}
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
static void Reflect(AZ::ReflectContext* reflection);
AZStd::vector<ScriptEvents::ScriptEventsAssetRef> m_scriptEventAssets;
};
}
}
@@ -0,0 +1,196 @@
/*
* 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.
*
*/
#pragma once
#include "DefaultEventHandler.h"
#include <AzCore/RTTI/BehaviorContext.h>
namespace ScriptEvents
{
class BehaviorHandlerFactoryMethod : public AZ::BehaviorMethod
{
public:
AZ_CLASS_ALLOCATOR(BehaviorHandlerFactoryMethod, AZ::SystemAllocator, 0);
// The result parameter takes the 0th index
enum ParameterIndex
{
StartNamedArgument = 1
};
BehaviorHandlerFactoryMethod(AZ::BehaviorEBus* ebus, AZ::BehaviorContext* behaviorContext, const AZStd::string& name)
: AZ::BehaviorMethod(behaviorContext)
, m_ebus(ebus)
, m_name(name)
{}
~BehaviorHandlerFactoryMethod() override
{}
bool Call([[maybe_unused]] AZ::BehaviorValueParameter* arguments, [[maybe_unused]] unsigned int numArguments, [[maybe_unused]] AZ::BehaviorValueParameter* result = nullptr) const override
{
return false;
}
bool HasResult() const override
{
return false;
}
bool IsMember() const override
{
return false;
}
bool HasBusId() const override
{
return false;
}
const AZ::BehaviorParameter* GetBusIdArgument() const override
{
return nullptr;
}
void OverrideParameterTraits(size_t /*index*/, AZ::u32 /*addTraits*/, AZ::u32 /*removeTraits*/) override
{
}
size_t GetNumArguments() const override
{
return 0;
}
size_t GetMinNumberOfArguments() const override
{
return 0;
}
const AZ::BehaviorParameter* GetArgument(size_t /*index*/) const override
{
return nullptr;
}
const AZStd::string* GetArgumentName(size_t /*index*/) const override
{
return nullptr;
}
void SetArgumentName(size_t /*index*/, const AZStd::string& /*name*/) override
{
}
const AZStd::string* GetArgumentToolTip(size_t /*index*/) const override
{
return nullptr;
}
void SetArgumentToolTip(size_t /*index*/, const AZStd::string& /*name*/) override
{
}
void SetDefaultValue(size_t /*index*/, AZ::BehaviorDefaultValuePtr /*defaultValue*/) override
{
}
AZ::BehaviorDefaultValuePtr GetDefaultValue(size_t /*index*/) const override
{
return nullptr;
}
const AZ::BehaviorParameter* GetResult() const override
{
return nullptr;
}
protected:
AZStd::string m_name;
AZ::BehaviorEBus* m_ebus;
};
class DefaultBehaviorHandlerCreator : public BehaviorHandlerFactoryMethod
{
public:
AZ_CLASS_ALLOCATOR(DefaultBehaviorHandlerCreator, AZ::SystemAllocator, 0);
DefaultBehaviorHandlerCreator(AZ::BehaviorEBus* ebus, AZ::BehaviorContext* behaviorContext, const AZStd::string& name)
: BehaviorHandlerFactoryMethod(ebus, behaviorContext, name)
{
}
bool Call(AZ::BehaviorValueParameter* arguments, unsigned int numArguments, AZ::BehaviorValueParameter* result = nullptr) const override
{
const ScriptEvents::ScriptEvent* scriptEventDefinition = nullptr;
if (numArguments > 0)
{
scriptEventDefinition = static_cast<const ScriptEvents::ScriptEvent*>(arguments[0].GetValueAddress());
}
if (result)
{
// the result is expecting a bus handler, store the functor* as the result's value
*static_cast<void**>(result->m_value) = aznew DefaultBehaviorHandler(m_ebus, scriptEventDefinition);
return true;
}
return false;
}
bool HasResult() const override
{
return true;
}
bool IsMember() const override
{
return true;
}
};
class DefaultBehaviorHandlerDestroyer : public BehaviorHandlerFactoryMethod
{
public:
AZ_CLASS_ALLOCATOR(DefaultBehaviorHandlerDestroyer, AZ::SystemAllocator, 0);
DefaultBehaviorHandlerDestroyer(AZ::BehaviorEBus* ebus, AZ::BehaviorContext* behaviorContext, const AZStd::string& name)
: BehaviorHandlerFactoryMethod(ebus, behaviorContext, name)
{
}
bool Call(AZ::BehaviorValueParameter* arguments, [[maybe_unused]] unsigned int numArguments, [[maybe_unused]] AZ::BehaviorValueParameter* result = nullptr) const override
{
AZ_Assert(arguments, "Must pass in the handler to delete");
if (arguments)
{
// The first argument is the handler that needs to be deleted
delete *arguments[0].GetAsUnsafe<DefaultBehaviorHandler*>();
}
return true;
}
bool HasResult() const override
{
return true;
}
bool IsMember() const override
{
return true;
}
};
}
@@ -0,0 +1,155 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "DefaultEventHandler.h"
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventDefinition.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
#include "ScriptEventsBindingBus.h"
namespace ScriptEvents
{
DefaultBehaviorHandler::~DefaultBehaviorHandler()
{
Disconnect();
}
DefaultBehaviorHandler::DefaultBehaviorHandler(AZ::BehaviorEBus* ebus, const ScriptEvents::ScriptEvent* scriptEventDefinition)
: m_ebus(ebus)
{
m_busNameId = AZ::Uuid::CreateName(m_ebus->m_name.c_str());
for (const auto& eventPair : m_ebus->m_events)
{
const auto* event = (eventPair.second.m_event != nullptr) ? eventPair.second.m_event : eventPair.second.m_broadcast;
if (event)
{
AZ::BehaviorEBusHandler::BusForwarderEvent eventForwarder;
eventForwarder.m_name = event->m_name.c_str();
eventForwarder.m_parameters.push_back(*event->GetResult());
// If a definition is provided, for each method check all the versions until we find the one that matches.
// This is necessary because we need to use the versioned property's ID as the m_eventId of the forwarder
// for Script Canvas.
if (scriptEventDefinition)
{
for (auto& method : scriptEventDefinition->GetMethods())
{
AZStd::string name = method.GetName();
if (name.compare(event->m_name) == 0)
{
// We need the of this property as they will all have the same ID.
eventForwarder.m_eventId = AZ::Crc32(method.GetNameProperty().GetId().ToString<AZStd::string>().c_str());
}
else
{
// If the event name doesn't match the current version, check all other versions.
for (const auto& version : method.GetNameProperty().GetVersions())
{
AZStd::string versionName;
if (version.Get<AZStd::string>(versionName))
{
if (versionName.compare(event->m_name) == 0)
{
// We need the ID of any version of this property as they will all have the same ID.
eventForwarder.m_eventId = AZ::Crc32(version.GetId().ToString<AZStd::string>().c_str());
}
}
}
}
}
}
// As a fallback, we'll use the Crc32 of the event name
if (eventForwarder.m_eventId == AZ::Crc32())
{
eventForwarder.m_eventId = AZ::Crc32(event->m_name.c_str());
}
// this extra parameter is only needed for broadcast only buses
bool isAddressable = Types::IsAddressableType(m_ebus->m_idParam.m_typeId);
if (!isAddressable)
{
eventForwarder.m_parameters.push_back(m_ebus->m_idParam);
}
size_t argumentCount = event->GetNumArguments();
for (size_t i = 0; i < argumentCount; ++i)
{
eventForwarder.m_parameters.push_back(*event->GetArgument(i));
}
eventForwarder.m_isFunctionGeneric = true;
m_events.push_back(eventForwarder);
}
}
}
int DefaultBehaviorHandler::GetFunctionIndex(const char* name) const
{
if (m_ebus->m_events.find(name) == m_ebus->m_events.end())
{
AZ_Error("Script Events", false, "No function with the name %s found.", name);
return -1;
}
return static_cast<int>(AZStd::distance(m_ebus->m_events.begin(), m_ebus->m_events.find(name)));
}
bool DefaultBehaviorHandler::Connect(AZ::BehaviorValueParameter* address)
{
if (address)
{
AZ_Assert(address->m_typeId == m_ebus->m_idParam.m_typeId, "Error EBus %s requires an address of type %s (%s), received %s (%s)",
m_ebus->m_name.c_str(), m_ebus->m_idParam.m_name, m_ebus->m_idParam.m_typeId.ToString<AZStd::string>().c_str(), address->m_name, address->m_typeId.ToString<AZStd::string>().c_str());
if (!m_address.m_value && address->m_typeId != azrtti_typeid<void>())
{
// get the behavior class for our address
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(address->m_typeId);
AZ_Warning("DefaultBehaviorHandler", behaviorClass, "%s is not a valid reflected class", address->m_name);
if (behaviorClass)
{
// cache a copy of the bus address for invoking events
static_cast<AZ::BehaviorParameter&>(m_address) = m_ebus->m_idParam;
m_address.m_value = behaviorClass->Allocate();
behaviorClass->m_cloner(m_address.m_value, address->m_value, nullptr);
}
}
}
Internal::BindingRequestBus::Event(m_busNameId, &Internal::BindingRequest::Connect, &m_address, this);
return true;
}
void DefaultBehaviorHandler::Disconnect()
{
Internal::BindingRequestBus::Event(m_busNameId, &Internal::BindingRequest::Disconnect, &m_address, this);
if (m_address.m_value)
{
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(m_address.m_typeId);
AZ_Assert(behaviorClass, "Did not find class %s when disconnecting DefaultBehaviorHandler", m_ebus->m_name.c_str());
behaviorClass->m_destructor(m_address.m_value, behaviorClass->m_userData);
behaviorClass->Deallocate(m_address.m_value);
m_address.m_value = nullptr;
}
}
}
@@ -0,0 +1,70 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/RTTI/BehaviorContext.h>
namespace ScriptEvents
{
class ScriptEvent;
class ScriptEventsHandler
: public AZ::BehaviorEBusHandler
{
public:
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
AZ_RTTI(ScriptEventsHandler, "{4272AECB-F1A7-4B22-8537-2EE6492EC132}", AZ::BehaviorEBusHandler);
AZ_CLASS_ALLOCATOR(ScriptEventsHandler, AZ::SystemAllocator, 0);
virtual AZ::BehaviorValueParameter* GetBusId() = 0;
bool IsConnected() override
{
return false;
}
bool IsConnectedId(AZ::BehaviorValueParameter*) override
{
return false;
}
};
class DefaultBehaviorHandler : public ScriptEventsHandler
{
public:
AZ_RTTI(DefaultBehaviorHandler, "{0AB58075-EE4F-49D7-83D4-E1250CC4471E}", ScriptEventsHandler);
AZ_CLASS_ALLOCATOR(DefaultBehaviorHandler, AZ::SystemAllocator, 0);
DefaultBehaviorHandler(AZ::BehaviorEBus* ebus, const ScriptEvents::ScriptEvent*);
~DefaultBehaviorHandler() override;
//////////////////////////////////////////////////////////////////////////
// EventsHandler
AZ::BehaviorValueParameter* GetBusId() override { return &m_address; }
//////////////////////////////////////////////////////////////////////////
/// AZ::BehaviorEBusHandler
int GetFunctionIndex(const char* name) const override;
bool Connect(AZ::BehaviorValueParameter* address = nullptr) override;
void Disconnect() override;
private:
AZ::BehaviorValueParameter m_address;
AZ::Uuid m_busNameId;
AZ::BehaviorEBus* m_ebus;
};
}
@@ -0,0 +1,287 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "DefaultEventHandler.h"
#include <ScriptEvents/ScriptEventTypes.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace ScriptEvents
{
namespace
{
void Call(const AZ::BehaviorEBusHandler::BusForwarderEvent& forwarderEvent, int functionIndex, const Internal::BindingRequest::BindingParameters& parameter)
{
const AZ::u32 referenceTypes = AZ::BehaviorParameter::TR_POINTER & AZ::BehaviorParameter::TR_REFERENCE;
if (parameter.m_returnValue && !(parameter.m_returnValue->m_traits & referenceTypes))
{
AZ::BehaviorValueParameter returnValue(*parameter.m_returnValue);
reinterpret_cast<AZ::BehaviorEBusHandler::GenericHookType>(forwarderEvent.m_function)(forwarderEvent.m_userData, forwarderEvent.m_name, functionIndex, &returnValue, parameter.m_parameterCount, parameter.m_parameters);
// check for BehaviorClass type that must be cloned back to storage pointed to by m_returnValue.GetValueAddress(), regardless if GenericHookType() returned a pointer
if (returnValue.GetValueAddress() != parameter.m_returnValue->GetValueAddress())
{
if (returnValue.GetValueAddress())
{
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(parameter.m_returnValue->m_typeId);
if (behaviorClass && behaviorClass->m_cloner)
{
behaviorClass->m_cloner(parameter.m_returnValue->GetValueAddress(), returnValue.GetValueAddress(), nullptr);
}
else if (behaviorClass)
{
AZ_Error("ScriptEvents", false, "A ScriptEvent returned a class without a supported cloning function. Supply a cloning function for: %s.", behaviorClass->m_name.data());
}
else
{
AZ_Error("ScriptEvents", false, "A ScriptEvent returned a class that is not exposed to BehaviorContext.");
}
}
else
{
AZ_Error("ScriptCanvas", false, "A ScriptEvent call was supposed to return a value and returned none.");
}
}
}
else
{
reinterpret_cast<AZ::BehaviorEBusHandler::GenericHookType>(forwarderEvent.m_function)(forwarderEvent.m_userData, forwarderEvent.m_name, functionIndex, parameter.m_returnValue, parameter.m_parameterCount, parameter.m_parameters);
}
}
//! Searches the behavior context for a specified equal operator implementation
//! for the given behavior class
AZ::BehaviorMethod* FindEqualityOperatorMethod(const AZ::BehaviorClass* behaviorClass)
{
AZ_Assert(behaviorClass, "Invalid AZ::BehaviorClass submitted to FindEqualityOperatorMethod");
for (const auto& equalMethodCandidatePair : behaviorClass->m_methods)
{
const AZ::AttributeArray& attributes = equalMethodCandidatePair.second->m_attributes;
for (const auto& attributePair : attributes)
{
if (attributePair.second->RTTI_IsTypeOf(AZ::AzTypeInfo<AZ::AttributeData<AZ::Script::Attributes::OperatorType>>::Uuid()))
{
const auto& attributeData = AZ::RttiCast<AZ::AttributeData<AZ::Script::Attributes::OperatorType>*>(attributePair.second);
if (attributeData->Get(nullptr) == AZ::Script::Attributes::OperatorType::Equal)
{
return equalMethodCandidatePair.second;
}
}
}
}
return nullptr;
}
}
ScriptEventBinding::ScriptEventBinding(AZ::BehaviorContext* context, AZStd::string_view scriptEventName, const AZ::Uuid& addressType)
: m_context(context)
, m_scriptEventName(scriptEventName)
{
m_busBindingAddress = AZ::Uuid::CreateName(scriptEventName.data());
Internal::BindingRequestBus::Handler::BusConnect(m_busBindingAddress);
if (ScriptEvents::Types::IsAddressableType(addressType))
{
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(addressType);
m_equalityOperatorMethod = FindEqualityOperatorMethod(behaviorClass);
AZ_Assert(m_equalityOperatorMethod, "Address type %s for %s must implement an equality operator, see AZ::Script::Attributes::OperatorType::Equal", addressType.ToString<AZStd::string>().c_str(), scriptEventName.data());
}
}
ScriptEventBinding::~ScriptEventBinding()
{
Internal::BindingRequestBus::Handler::BusDisconnect(m_busBindingAddress);
}
void ScriptEventBinding::Bind(const BindingParameters& parameter)
{
// If an address is not provided, this script event will be broadcast
if (!parameter.m_address || parameter.m_address->m_typeId.IsNull())
{
for (ScriptEventsHandler* eventHandler : m_broadcasts)
{
int functionIndex = eventHandler->GetFunctionIndex(parameter.m_eventName.data());
if (functionIndex >= 0 && functionIndex < eventHandler->GetEvents().size())
{
const AZ::BehaviorEBusHandler::BusForwarderEvent& forwarderEvent = eventHandler->GetEvents()[functionIndex];
if (!forwarderEvent.m_function)
{
// Note, this may be OK if it happened in Script Canvas, we can't reasonably expect every event to be handled, I just need to be sure that
// if there is a handler node that we don't get this.
AZ_WarningOnce("Script Events", forwarderEvent.m_function, "Function %s not found for event: %s in script: %s - if needed, provide an implementation.", parameter.m_eventName.data(), m_scriptEventName.data(), eventHandler->GetScriptPath().c_str());
}
else
{
Call(forwarderEvent, functionIndex, parameter);
}
}
}
// Broadcast event to all
for (EventBindingEntry& handlerEntry : m_events)
{
for (ScriptEventsHandler* eventHandler : handlerEntry.second)
{
int functionIndex = eventHandler->GetFunctionIndex(parameter.m_eventName.data());
if (functionIndex >= 0 && functionIndex < eventHandler->GetEvents().size())
{
const AZ::BehaviorEBusHandler::BusForwarderEvent& forwarderEvent = eventHandler->GetEvents()[functionIndex];
if (!forwarderEvent.m_function)
{
AZ_WarningOnce("Script Events", forwarderEvent.m_function, "Function %s not found for event: %s in script: %s - if needed, provide an implementation.", parameter.m_eventName.data(), m_scriptEventName.data(), eventHandler->GetScriptPath().c_str());
}
else
{
Call(forwarderEvent, functionIndex, parameter);
}
}
}
}
}
else
{
size_t addressHash = GetAddressHash(parameter.m_address);
EventMap::iterator eventIterator = m_events.find(addressHash);
if (eventIterator != m_events.end())
{
// look for exact matches within the hash bucket
// Handlers may be disconnected as a result of this operation, making a copy here to avoid iterating over removed elements of m_events
EventSet events = eventIterator->second;
for (ScriptEventsHandler* handler : events)
{
AZ::BehaviorClass* addressTypeClass = m_context->m_typeToClassMap.at(parameter.m_address->m_typeId);
bool isEqual = true;
// use the default comparer for classes exposed through behaviorContext->Class<SomeType>(
if (m_equalityOperatorMethod)
{
AZ::BehaviorValueParameter addresses[2];
// we are going to call an equality operator on this, but the behavior method expects the args to be continuous
// capture the value of the address
if (m_equalityOperatorMethod->GetArgument(0)->m_traits & AZ::BehaviorParameter::TR_POINTER)
{
addresses[0].m_value = &parameter.m_address->m_value;
}
else
{
addresses[0].m_value = parameter.m_address->m_value;
}
*static_cast<AZ::BehaviorParameter*>(&addresses[0]) = static_cast<const AZ::BehaviorParameter&>(*parameter.m_address);
addresses[0].m_tempData = parameter.m_address->m_tempData;
addresses[0].m_traits = m_equalityOperatorMethod->GetArgument(0)->m_traits;
// capture the value stored in the handler
if (m_equalityOperatorMethod->GetArgument(1)->m_traits & AZ::BehaviorParameter::TR_POINTER)
{
addresses[1].m_value = &handler->GetBusId()->m_value;
}
else
{
addresses[1].m_value = handler->GetBusId()->m_value;
}
*static_cast<AZ::BehaviorParameter*>(&addresses[1]) = static_cast<const AZ::BehaviorParameter&>(*handler->GetBusId());
addresses[1].m_tempData = handler->GetBusId()->m_tempData;
addresses[1].m_traits = m_equalityOperatorMethod->GetArgument(1)->m_traits;
AZ::BehaviorValueParameter addressMatch;
addressMatch.Set(&isEqual);
m_equalityOperatorMethod->Call(addresses, 2, &addressMatch);
}
else if (addressTypeClass->m_equalityComparer)
{
isEqual = addressTypeClass->m_equalityComparer(parameter.m_address->m_value, handler->GetBusId()->m_value, nullptr);
}
if (isEqual)
{
int functionIndex = handler->GetFunctionIndex(parameter.m_eventName.data());
if (functionIndex >= 0 && functionIndex < handler->GetEvents().size())
{
AZ::BehaviorEBusHandler::BusForwarderEvent forwarderEvent = handler->GetEvents()[functionIndex];
AZ_WarningOnce("Script Events", forwarderEvent.m_function, "Function %s not found for event: %s in script: %s - if needed, provide an implementation.", parameter.m_eventName.data(), m_scriptEventName.data(), handler->GetScriptPath().c_str());
if (forwarderEvent.m_function)
{
Call(forwarderEvent, functionIndex, parameter);
}
}
}
}
}
}
}
void ScriptEventBinding::Connect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler)
{
AZ_Warning("Script Event", address, "%s: Address was not specified when connecting,", m_scriptEventName.data());
if (address && address->m_value)
{
size_t addressHash = GetAddressHash(address);
m_events[addressHash].insert(handler);
}
else
{
m_broadcasts.insert(handler);
}
}
void ScriptEventBinding::Disconnect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler)
{
if (address->m_value)
{
size_t addressHash = GetAddressHash(address);
auto eventIter = m_events.find(addressHash);
if (eventIter != m_events.end())
{
eventIter->second.erase(handler);
if (eventIter->second.empty())
{
m_events.erase(addressHash);
}
}
}
else
{
m_broadcasts.erase(handler);
for (EventBindingEntry& eventBindingEntry : m_events)
{
eventBindingEntry.second.erase(handler);
}
}
}
void ScriptEventBinding::RemoveHandler(ScriptEventsHandler* handler)
{
m_broadcasts.erase(handler);
}
size_t ScriptEventBinding::GetAddressHash(const AZ::BehaviorValueParameter* address)
{
const AZ::BehaviorClass* behaviorClass = AZ::BehaviorContextHelper::GetClass(address->m_typeId);
AZ_Assert(behaviorClass, "The specified type %s is not in the Behavior Context, make sure it is reflected.", address->m_name);
return behaviorClass->m_valueHasher(address->m_value);
}
}
@@ -0,0 +1,66 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/std/containers/set.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/ScriptEventsBindingBus.h>
namespace ScriptEvents
{
class ScriptEventsHandler;
class ScriptEventBinding
: Internal::BindingRequestBus::Handler
{
public:
AZ_TYPE_INFO(ScriptEventBinding, "{E0DDA446-656D-41D6-8BEC-42B6EA57DD7D}");
AZ_CLASS_ALLOCATOR(ScriptEventBinding, AZ::SystemAllocator, 0);
ScriptEventBinding(AZ::BehaviorContext* context, AZStd::string_view scriptEventName, const AZ::Uuid& addressType);
AZStd::string_view GetScriptEventName() const { return m_scriptEventName; }
virtual ~ScriptEventBinding();
protected:
// Internal::BindingRequestBus
void Bind(const BindingParameters&) override;
void Connect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler) override;
void Disconnect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler) override;
void RemoveHandler(ScriptEventsHandler* handler) override;
////
// Behavior classes have a hash identifier that we will use to bind script events
size_t GetAddressHash(const AZ::BehaviorValueParameter* address);
// The equality operator method for the script event's address type (type must provide this operator to be used as script event address)
AZ::BehaviorMethod* m_equalityOperatorMethod;
// Script Events without a specified address will be broadcast to
using EventSet = AZStd::set<ScriptEventsHandler*>;
EventSet m_broadcasts;
using EventBindingEntry = AZStd::pair<size_t, AZStd::set<ScriptEventsHandler*>>;
using EventMap = AZStd::unordered_map<size_t, AZStd::set<ScriptEventsHandler*>>;
EventMap m_events;
AZStd::string_view m_scriptEventName;
AZ::BehaviorContext* m_context;
AZ::Uuid m_busBindingAddress;
};
}
@@ -0,0 +1,132 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventBroadcast.h"
#include "ScriptEventsBindingBus.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Asset/AssetCommon.h>
#include <ScriptEvents/ScriptEvent.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
namespace ScriptEvents
{
ScriptEventBroadcast::ScriptEventBroadcast(AZ::BehaviorContext* behaviorContext, const ScriptEvent& definition, AZStd::string eventName)
: AZ::BehaviorMethod(behaviorContext)
, m_returnType(AZ::Uuid::CreateNull())
{
m_name = AZStd::move(eventName);
const AZStd::string& busName = definition.GetName();
m_busBindingId = AZ::Uuid::CreateName(busName.c_str());
Method method;
if (!definition.FindMethod(m_name, method))
{
AZ_Warning("Script Events", false, "Method %s was not in Script Event: %s", m_name.c_str(), m_name.c_str());
}
if (!method.GetReturnTypeProperty().IsEmpty())
{
method.GetReturnTypeProperty().Get(m_returnType);
m_returnType = method.GetReturnType();
}
m_result.m_name = "Result";
Internal::Utils::BehaviorParameterFromType(m_returnType, false, m_result);
ReserveArguments(method.GetParameters().size() + 1);
size_t index = 1;
for (const Parameter& parameter : method.GetParameters())
{
const AZStd::string& argumentName = parameter.GetName();
if (parameter.GetType().IsNull())
{
AZ_Warning("Script Events", false, "Argument type for parameter %s cannot be null", argumentName.c_str());
continue;
}
SetArgumentName(index, argumentName);
m_behaviorParameters.push_back();
Internal::Utils::BehaviorParameterFromParameter(behaviorContext, parameter, m_argumentNames[index].c_str(), m_behaviorParameters.back());
const AZStd::string& tooltip = parameter.GetTooltip();
if (!tooltip.empty())
{
SetArgumentToolTip(index, tooltip.c_str());
}
++index;
}
//AZ_TracePrintf("Script Events", "Script Broadcast Method: %s %s::%s (Arguments: %zu)\n", m_returnType.ToString<AZStd::string>().c_str(), busName.c_str(), m_name.c_str(), method.GetParameters().size());
}
bool ScriptEventBroadcast::Call(AZ::BehaviorValueParameter* params, unsigned int paramCount, AZ::BehaviorValueParameter* returnValue) const
{
Internal::BindingRequest::BindingParameters parameters;
parameters.m_eventName = m_name;
parameters.m_address = nullptr;
parameters.m_parameters = params;
parameters.m_parameterCount = paramCount;
parameters.m_returnValue = returnValue;
Internal::BindingRequestBus::Event(m_busBindingId, &Internal::BindingRequest::Bind, parameters);
if (returnValue && returnValue->m_onAssignedResult)
{
returnValue->m_onAssignedResult();
}
return true;
}
void ScriptEventBroadcast::ReserveArguments(size_t numArguments)
{
m_behaviorParameters.reserve(numArguments);
m_argumentNames.resize(numArguments);
m_argumentToolTips.resize(numArguments);
}
void ScriptEventBroadcast::SetArgumentName(size_t index, const AZStd::string& name)
{
if (index >= m_argumentNames.size())
{
m_argumentNames.resize(index + 1);
}
m_argumentNames[index] = name;
}
size_t ScriptEventBroadcast::GetMinNumberOfArguments() const
{
// Iterate from end of parameters and count the number of consecutive valid BehaviorValue objects
size_t numDefaultArguments = 0;
for (size_t i = GetNumArguments() - 1; i >= 0 && GetDefaultValue(i); --i, ++numDefaultArguments)
{
}
return GetNumArguments() - numDefaultArguments;
}
AZ::BehaviorDefaultValuePtr ScriptEventBroadcast::GetDefaultValue(size_t) const
{
// Default values for Script Events are not implemented.
return nullptr;
}
}
@@ -0,0 +1,95 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
#include <ScriptEvents/ScriptEventDefinition.h>
namespace AZ
{
class BehaviorContext;
struct BehaviorValueParameter;
}
namespace ScriptEvents
{
class ScriptEventBroadcast
: public AZ::BehaviorMethod
{
public:
AZ_TYPE_INFO(ScriptEventBroadcast, "{7C3DDD76-BECA-4A1D-8605-A72D6CF91051}");
AZ_CLASS_ALLOCATOR(ScriptEventBroadcast, AZ::SystemAllocator, 0);
ScriptEventBroadcast(AZ::BehaviorContext* behaviorContext, const ScriptEvent& definition, AZStd::string eventName);
bool Call(AZ::BehaviorValueParameter* params, unsigned int paramCount, AZ::BehaviorValueParameter* returnValue) const override;
bool HasResult() const override { return !m_returnType.IsNull(); }
bool IsMember() const override { return false; }
void ReserveArguments(size_t numArguments);
size_t GetNumArguments() const override { return m_behaviorParameters.size(); }
const AZ::BehaviorParameter* GetArgument(size_t index) const override
{
if (index >= m_behaviorParameters.size())
{
AZ_Warning("Script Events", false, "Index out of bounds while trying to get method argument (%s, %d)", m_name.c_str(), index);
return nullptr;
}
return &m_behaviorParameters[index];
}
const AZStd::string* GetArgumentName(size_t index) const override { return &m_argumentNames[index]; }
void SetArgumentName(size_t index, const AZStd::string& name) override;
const AZ::BehaviorParameter* GetResult() const override { return &m_result; }
bool HasBusId() const override { return false; }
const AZStd::string* GetArgumentToolTip(size_t index) const override { return &m_argumentToolTips[index]; }
void SetArgumentToolTip(size_t index, const AZStd::string& tooltip) override
{
if (index >= m_argumentToolTips.size())
{
m_argumentToolTips.resize(index + 1);
}
m_argumentToolTips[index] = tooltip;
}
const AZ::BehaviorParameter* GetBusIdArgument() const override { return nullptr; }
size_t GetMinNumberOfArguments() const override;
AZ::BehaviorDefaultValuePtr GetDefaultValue(size_t) const override;
void OverrideParameterTraits(size_t, AZ::u32, AZ::u32) override {}
void SetDefaultValue(size_t, AZ::BehaviorDefaultValuePtr) override {}
private:
AZ::Uuid m_returnType;
AZ::BehaviorValueParameter m_result;
AZStd::vector<AZStd::string> m_argumentNames;
AZStd::vector<AZStd::string> m_argumentToolTips;
AZStd::vector<AZ::BehaviorParameter> m_behaviorParameters;
AZ::Uuid m_busBindingId;
};
}
@@ -0,0 +1,142 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventMethod.h"
#include "ScriptEventsBindingBus.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Asset/AssetCommon.h>
#include <ScriptEvents/ScriptEvent.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
namespace ScriptEvents
{
ScriptEventMethod::ScriptEventMethod(AZ::BehaviorContext* behaviorContext, const ScriptEvent& definition, const AZStd::string eventName)
: AZ::BehaviorMethod(behaviorContext)
, m_busIdType(AZ::Uuid::CreateNull())
, m_returnType(AZ::Uuid::CreateNull())
, m_busBindingId()
{
m_name = eventName;
const AZStd::string& busName = definition.GetName();
m_busBindingId = AZ::Uuid::CreateName(busName.c_str());
m_busIdType = definition.GetAddressType();
Method method;
if (!definition.FindMethod(eventName, method))
{
AZ_Warning("Script Events", false, "Method %s not found in Script Event %s", eventName.data(), busName.c_str());
}
m_result.m_name = "Result";
if (!method.GetReturnTypeProperty().IsEmpty())
{
method.GetReturnTypeProperty().Get(m_returnType);
Internal::Utils::BehaviorParameterFromType(m_returnType, false, m_result);
}
ReserveArguments(method.GetParameters().size() + 1);
size_t index = 0;
// BehaviorContext Ebus Events require an Id, it is passed in as the first parameter to the method.
if (!m_busIdType.IsNull())
{
AZ::BehaviorValueParameter busId;
Internal::Utils::BehaviorParameterFromType(m_busIdType, true, busId);
m_behaviorParameters.emplace_back(busId);
SetArgumentName(index, busId.m_name);
SetArgumentToolTip(index, busId.m_name);
++index;
}
AZStd::vector<AZStd::string> tests;
for (const Parameter& parameter : method.GetParameters())
{
const AZStd::string& argumentName = parameter.GetName();
SetArgumentName(index, argumentName);
m_behaviorParameters.push_back();
Internal::Utils::BehaviorParameterFromParameter(behaviorContext, parameter, m_argumentNames[index].c_str(), m_behaviorParameters.back());
const AZStd::string& tooltip = parameter.GetTooltip();
if (!tooltip.empty())
{
SetArgumentToolTip(index, tooltip.data());
}
++index;
}
//AZ_TracePrintf("Script Events", "Script Method: %s %s::%s (Arguments: %d)\n", m_returnType.ToString<AZStd::string>().c_str(), busName.c_str(), eventName.data(), method.GetParameters().size());
}
bool ScriptEventMethod::Call(AZ::BehaviorValueParameter* params, unsigned int paramCount, AZ::BehaviorValueParameter* returnValue) const
{
Internal::BindingRequest::BindingParameters parameters;
parameters.m_eventName = m_name;
parameters.m_address = &params[0]; // The address is stored in the first parameter
parameters.m_parameters = params + 1;
parameters.m_parameterCount = paramCount - 1; // Minus the address
parameters.m_returnValue = returnValue;
Internal::BindingRequestBus::Event(m_busBindingId, &Internal::BindingRequest::Bind, parameters);
if (returnValue && returnValue->m_onAssignedResult)
{
returnValue->m_onAssignedResult();
}
return true;
}
void ScriptEventMethod::ReserveArguments(size_t numArguments)
{
m_behaviorParameters.reserve(numArguments);
m_argumentNames.resize(numArguments);
m_argumentToolTips.resize(numArguments);
}
void ScriptEventMethod::SetArgumentName(size_t index, const AZStd::string& name)
{
if (index >= m_argumentNames.size())
{
m_argumentNames.resize(index + 1);
}
m_argumentNames[index] = name;
}
size_t ScriptEventMethod::GetMinNumberOfArguments() const
{
// Iterate from end of parameters and count the number of consecutive valid BehaviorValue objects
size_t numDefaultArguments = 0;
for (size_t i = GetNumArguments() - 1; i >= 0 && GetDefaultValue(i); --i, ++numDefaultArguments)
{
}
return GetNumArguments() - numDefaultArguments;
}
AZ::BehaviorDefaultValuePtr ScriptEventMethod::GetDefaultValue(size_t) const
{
// Default values for Script Events are not implemented.
return nullptr;
}
}
@@ -0,0 +1,97 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/string/string_view.h>
#include <ScriptEvents/ScriptEventDefinition.h>
namespace AZ
{
class BehaviorContext;
struct BehaviorValueParameter;
}
namespace ScriptEvents
{
class ScriptEventMethod
: public AZ::BehaviorMethod
{
public:
AZ_TYPE_INFO(ScriptEventMethod, "{9C593217-5548-485C-89DF-A76228EBAD72}");
AZ_CLASS_ALLOCATOR(ScriptEventMethod, AZ::SystemAllocator, 0);
ScriptEventMethod(AZ::BehaviorContext* behaviorContext, const ScriptEvent& definition, const AZStd::string eventName);
bool Call(AZ::BehaviorValueParameter* params, unsigned int paramCount, AZ::BehaviorValueParameter* returnValue) const override;
bool HasResult() const override { return !m_returnType.IsNull() && m_returnType != azrtti_typeid<void>(); }
bool IsMember() const override { return false; }
void ReserveArguments(size_t numArguments);
size_t GetNumArguments() const override { return m_behaviorParameters.size(); }
const AZ::BehaviorParameter* GetArgument(size_t index) const override
{
if (index >= m_behaviorParameters.size())
{
AZ_Warning("Script Events", false, "Index out of bounds while trying to get method argument (%s, %d)", m_name.c_str(), index);
return nullptr;
}
return &m_behaviorParameters[index];
}
const AZStd::string* GetArgumentName(size_t index) const override { return &m_argumentNames[index]; }
void SetArgumentName(size_t index, const AZStd::string& name) override;
const AZ::BehaviorParameter* GetResult() const override { return &m_result; }
bool HasBusId() const override { return !m_busIdType.IsNull(); }
const AZStd::string* GetArgumentToolTip(size_t index) const override { return &m_argumentToolTips[index]; }
void SetArgumentToolTip(size_t index, const AZStd::string& tooltip) override
{
if (index >= m_argumentToolTips.size())
{
m_argumentToolTips.resize(index + 1);
}
m_argumentToolTips[index] = tooltip;
}
const AZ::BehaviorParameter* GetBusIdArgument() const override { return nullptr; }
size_t GetMinNumberOfArguments() const override;
AZ::BehaviorDefaultValuePtr GetDefaultValue(size_t) const override;
void OverrideParameterTraits(size_t, AZ::u32, AZ::u32) override {}
void SetDefaultValue(size_t, AZ::BehaviorDefaultValuePtr) override {}
private:
AZ::Uuid m_busIdType;
AZ::Uuid m_returnType;
AZ::BehaviorValueParameter m_result;
AZStd::vector<AZStd::string> m_argumentNames;
AZStd::vector<AZStd::string> m_argumentToolTips;
AZStd::vector<AZ::BehaviorParameter> m_behaviorParameters;
AZ::Uuid m_busBindingId;
};
}
@@ -0,0 +1,65 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/std/string/string.h>
namespace ScriptEvents
{
class ScriptEventsHandler;
namespace Internal
{
//! Script Events are bound to their respective handlers through Bind requests
class BindingRequest
: public AZ::EBusTraits
{
public:
struct BindingParameters
{
AZStd::string_view m_eventName;
AZ::BehaviorValueParameter* m_address;
AZ::BehaviorValueParameter* m_parameters;
AZ::u32 m_parameterCount;
AZ::BehaviorValueParameter* m_returnValue;
BindingParameters()
: m_address(nullptr)
, m_parameterCount(0)
, m_parameters(nullptr)
, m_returnValue(nullptr)
{}
};
//! Binding requests are done using a unique ID from the EBus/method name as the address
using BusIdType = AZ::Uuid;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
//! Request a bound event to be invoked given the parameters specified
virtual void Bind(const BindingParameters&) = 0;
virtual void Connect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler) = 0;
virtual void Disconnect(const AZ::BehaviorValueParameter* address, ScriptEventsHandler* handler) = 0;
virtual void RemoveHandler(ScriptEventsHandler*) = 0;
};
using BindingRequestBus = AZ::EBus<BindingRequest>;
}
}
@@ -0,0 +1,136 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "VersionedProperty.h"
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/Serialization/DynamicSerializableField.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Script/ScriptProperty.h>
namespace ScriptEventData
{
namespace Internal
{
void VersionedPropertyConstructor(VersionedProperty* self, AZ::ScriptDataContext& dc)
{
if (dc.GetNumArguments() == 0)
{
AZ_Warning("VersionedProperty", false, "Not enough arguments specified to construct VersionedProperty");
return;
}
if (dc.IsString(0))
{
AZStd::string data;
if (dc.ReadArg(0, data))
{
*self = VersionedProperty();
self->Set(data.c_str());
}
}
else
if (dc.IsRegisteredClass(0))
{
AZStd::any data;
if (dc.ReadArg(0, data))
{
*self = VersionedProperty();
self->Set(data);
}
}
else
if (dc.IsNumber(0))
{
double value;
if (dc.ReadArg(0, value))
{
*self = VersionedProperty();
self->Set(value);
}
}
}
void Set(VersionedProperty* self, AZ::ScriptDataContext& dc)
{
self->Set(dc);
}
void Get(VersionedProperty* self, AZ::ScriptDataContext& dc)
{
self->Get(dc);
}
}
VersionedProperty::VersionedProperty(AZ::ScriptDataContext& dc)
{
Internal::VersionedPropertyConstructor(this, dc);
}
void VersionedProperty::Reflect(AZ::ReflectContext* context)
{
VoidType::Reflect(context);
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<VersionedProperty>()
->Version(4)
->Field("m_id", &VersionedProperty::m_id)
->Field("m_label", &VersionedProperty::m_label)
->Field("m_version", &VersionedProperty::m_version)
->Field("m_versions", &VersionedProperty::m_versions)
->Field("m_data", &VersionedProperty::m_data)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<VersionedProperty>("VersionedProperty", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::ChildNameLabelOverride, &VersionedProperty::GetLabel)
//->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(0, &VersionedProperty::m_data, "", "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ScriptEventData::VersionedProperty>()
->Constructor<AZ::ScriptDataContext&>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Method("Set", &Internal::Set)
->Method("Get", &Internal::Get)
;
}
}
void VersionedProperty::PreSave()
{
NewVersion();
}
void VersionedProperty::Set(AZ::ScriptDataContext& dc)
{
VersionedProperty& newVersion = NewVersion();
Internal::VersionedPropertyConstructor(&newVersion, dc);
}
void VersionedProperty::Get(AZ::ScriptDataContext& dc)
{
AZ::ScriptValue<AZStd::any>::StackPush(dc.GetScriptContext()->NativeContext(), m_data);
dc.PushResult(m_data);
}
}
@@ -0,0 +1,315 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/std/sort.h>
#include <AzCore/Serialization/AZStdAnyDataContainer.inl>
namespace ScriptEventData
{
struct VoidType
{
AZ_TYPE_INFO(VoidType, "{BFF11497-FBD1-460A-B21F-D4519B9123ED}");
static void Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<VoidType>();
}
}
};
class VersionedProperty;
//! A VersionedProperty holds a default or starting value and a list of versions.
//! The list of versions is immutable until the moment the property is flattened.
//! Flattening a property discards all versioning information but the latest and can
//! be used when it is desired to reduce the data size footprint.
//! Keeping the versioning data around is incredibly handly for backwards compatibility.
class VersionedProperty
{
public:
AZ_RTTI(VersionedProperty, "{828CA9C0-32F1-40B3-8018-EE7C3C38192A}");
AZ_CLASS_ALLOCATOR(VersionedProperty, AZ::SystemAllocator, 0);
VersionedProperty()
: m_id(AZ::Uuid::CreateRandom())
, m_version(0)
, m_backup(nullptr)
{
m_label = "MISSING_LABEL";
}
VersionedProperty(AZStd::string label)
: m_id(AZ::Uuid::CreateRandom())
, m_version(0)
, m_label(label)
, m_backup(nullptr)
{
}
VersionedProperty(const VersionedProperty& rhs)
{
m_id = rhs.m_id;
m_version = rhs.m_version;
m_label = rhs.m_label;
m_data = rhs.m_data;
m_versions = rhs.m_versions;
}
VersionedProperty(VersionedProperty&& rhs)
{
m_id = AZStd::move(rhs.m_id);
m_version = AZStd::move(rhs.m_version);
m_label = AZStd::move(rhs.m_label);
m_data = AZStd::move(rhs.m_data);
m_versions.swap(rhs.m_versions);
}
VersionedProperty& operator=(const VersionedProperty& rhs)
{
if (this != &rhs)
{
m_id = rhs.m_id;
m_version = rhs.m_version;
m_label = rhs.m_label;
m_data = rhs.m_data;
m_versions.assign(rhs.m_versions.begin(), rhs.m_versions.end());
}
return *this;
}
VersionedProperty(AZ::ScriptDataContext&);
virtual ~VersionedProperty()
{
m_data.clear();
delete m_backup;
m_versions.clear();
}
AZ::Uuid GetType() const
{
return m_data.type();
}
static VersionedProperty MakeVoid()
{
VersionedProperty property = VersionedProperty("Void");
property.Set<const VoidType>(VoidType {});
return AZStd::ref(property);
}
template <typename T>
static VersionedProperty Make(T t, const char* label)
{
VersionedProperty p(label);
p.Set(t);
return p;
}
void SetLabel(const char* label)
{
m_label = label;
}
// Return by value
AZStd::string GetLabel() const { return m_label; }
bool operator==(const VersionedProperty& rhs) const
{
return AZ::Helpers::CompareAnyValue(m_data, rhs.m_data);
}
AZStd::string ToString() const
{
return m_id.ToString<AZStd::string>();
}
bool operator!=(const VersionedProperty& rhs) const
{
return !operator==(rhs);
}
static void Reflect(AZ::ReflectContext* context);
void IncreaseVersion() { m_version++; }
void Get(AZ::ScriptDataContext& dc);
void Set(AZ::ScriptDataContext& dc);
bool IsEmpty() const
{
return m_data.empty();
}
struct VersionSort
{
inline bool operator() (const VersionedProperty& a, const VersionedProperty& b)
{
return (a.m_version > b.m_version);
}
};
//! Data can only be set into a property through this function.
template <typename T>
void Set(T& data)
{
m_data = AZStd::any(data);
}
//! Returns the latest version of the property
template <typename T>
const T* Get() const
{
if (m_data.empty())
{
return nullptr;
}
return AZStd::any_cast<T>(&m_data);
}
template <typename T>
bool Get(T& out) const
{
if (!m_data.empty())
{
out = AZStd::any_cast<T>(m_data);
return true;
}
return false;
}
template <typename T>
T* Get()
{
if (m_data.empty())
{
return nullptr;
}
return AZStd::any_cast<T>(&m_data);
}
void Set(const char* str)
{
AZStd::string text = str;
Set<const AZStd::string>(AZStd::move(text));
}
//! Creates a new version of the desired property.
VersionedProperty& NewVersion()
{
if (m_backup)
{
m_backup->m_versions.clear(); // Do not store the backup version history, we only need the property, otherwise this leads to exponential growth
VersionedProperty copy = *m_backup;
m_versions.push_back(AZStd::move(copy));
delete m_backup;
m_backup = nullptr;
++m_version;
}
return *this;
}
void OnPropertyChange()
{
if (!m_backup)
{
m_backup = aznew VersionedProperty(*this);
}
}
//! Applies the latest version as the default and clears the versioned information.
//! Warning: This operation is intentionally destructive, if the asset is saved after flattening
//! The versioning information will be lost, however, the asset size will be reduced.
void Flatten()
{
ApplyLatestVersions();
m_versions.clear();
}
//! Applies the latest version as the default, it can be used to make it easy to get access
//! to the latest version.
void ApplyLatestVersions()
{
for (const auto& property : m_versions)
{
if (property.m_version > m_version)
{
m_version = property.m_version;
m_data = property.m_data;
}
}
}
AZ::Uuid GetId() const { return m_id; }
AZ::u32 GetVersion() const { return m_version; }
const AZStd::vector<VersionedProperty>& GetVersions() const { return m_versions; }
const AZStd::any& GetRaw() const { return m_data; }
template <typename T>
void SetDefaultFromType()
{
m_data = AZStd::make_any<T>();
}
void PreSave();
private:
AZ::Uuid m_id;
AZ::u32 m_version;
AZStd::any m_data;
AZStd::string m_label;
AZStd::vector<VersionedProperty> m_versions;
VersionedProperty* m_backup = nullptr;
};
//! Given a class that may hold any VersionedProperties, iterate over its elements and
//! if any elements are VersionedProperty, they will be flattened.
template <typename T>
void FlattenVersionedPropertiesInObject(AZ::SerializeContext* serializeContext, T* obj)
{
serializeContext->EnumerateObject(obj,
[](void *instance,
const AZ::SerializeContext::ClassData *classData,
[[maybe_unused]] const AZ::SerializeContext::ClassElement *classElement) -> bool
{
if (classData->m_typeId == azrtti_typeid<VersionedProperty>())
{
auto property = reinterpret_cast<VersionedProperty*>(instance);
if (property != nullptr)
{
property->Flatten();
}
}
return true;
},
[]() -> bool { return true; },
AZ::SerializeContext::ENUM_ACCESS_FOR_READ, nullptr);
}
}
@@ -0,0 +1,328 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEvent.h"
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventFundamentalTypes.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h>
namespace ScriptEvents
{
static AZ::Outcome<bool, AZStd::string> IsAddressableTypeWithError(const AZ::Uuid& uuid)
{
static AZStd::pair<AZ::Uuid, const char*> unsupportedTypes[] = {
{ AZ::Uuid::CreateNull(), "null" },
{ azrtti_typeid<void>(), "void" },
{ azrtti_typeid<float>(), "float" },
{ azrtti_typeid<double>(), "double" } // Due to precision issues, floating point numbers make poor address types
};
for (auto& unsupportedType : unsupportedTypes)
{
if (unsupportedType.first == uuid)
{
return AZ::Failure(AZStd::string::format("The type %s with id %s is not supported as an address type.", unsupportedType.second, uuid.ToString<AZStd::string>().c_str()));
}
}
return AZ::Success(true);
}
namespace Internal
{
void Utils::BehaviorParameterFromParameter(AZ::BehaviorContext* behaviorContext, const Parameter& parameter, const char* name, AZ::BehaviorParameter& outParameter)
{
AZ::Uuid typeId = parameter.GetType();
outParameter.m_azRtti = nullptr;
outParameter.m_traits = AZ::BehaviorParameter::TR_NONE;
const FundamentalTypes* fundamentalTypes = nullptr;
ScriptEventBus::BroadcastResult(fundamentalTypes, &ScriptEventRequests::GetFundamentalTypes);
if (typeId == azrtti_typeid<void>())
{
outParameter.m_name = name;
outParameter.m_typeId = typeId;
}
else if (fundamentalTypes->IsFundamentalType(typeId))
{
const char* typeName = fundamentalTypes->FindFundamentalTypeName(typeId);
outParameter.m_name = name ? name : (typeName ? typeName : "UnknownType");
outParameter.m_typeId = typeId;
}
else if (const auto& behaviorClass = behaviorContext->m_typeToClassMap.at(typeId))
{
outParameter.m_azRtti = behaviorClass->m_azRtti;
outParameter.m_name = name ? name : behaviorClass->m_name.c_str();
outParameter.m_typeId = typeId;
}
else
{
outParameter.m_name = "ERROR";
outParameter.m_typeId = AZ::Uuid::CreateNull();
AZStd::string uuid;
typeId.ToString(uuid);
AZ_Error("Script Events", false, "Failed to find type %s for parameter %s", uuid.c_str(), name ? name : "UnknownType");
}
}
void Utils::BehaviorParameterFromType(AZ::Uuid typeId, bool addressable, AZ::BehaviorParameter& outParameter)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
AZ_Assert(behaviorContext, "Script Events require a valid Behavior Context");
outParameter.m_traits = AZ::BehaviorParameter::TR_NONE;
outParameter.m_typeId = typeId;
outParameter.m_azRtti = nullptr;
if (addressable)
{
AZ::Outcome<bool, AZStd::string> isAddressableType = IsAddressableTypeWithError(typeId);
if (!isAddressableType.IsSuccess())
{
AZ_Error("Script Events", false, "%s", isAddressableType.GetError().c_str());
return;
}
}
const FundamentalTypes* fundamentalTypes = nullptr;
ScriptEventBus::BroadcastResult(fundamentalTypes, &ScriptEventRequests::GetFundamentalTypes);
if (fundamentalTypes && fundamentalTypes->IsFundamentalType(typeId))
{
const char* typeName = fundamentalTypes->FindFundamentalTypeName(typeId);
outParameter.m_name = typeName;
}
else if (behaviorContext->m_typeToClassMap.find(typeId) != behaviorContext->m_typeToClassMap.end())
{
if (const auto& behaviorClass = behaviorContext->m_typeToClassMap.at(typeId))
{
outParameter.m_azRtti = behaviorClass->m_azRtti;
outParameter.m_name = behaviorClass->m_name.c_str();
}
}
else if (typeId == AZ::Uuid::CreateNull() || typeId == AZ::BehaviorContext::GetVoidTypeId())
{
outParameter.m_name = "void";
}
else
{
AZ_Warning("Script Events", false, "Invalid type specified for BehaviorParameter %s", typeId.ToString<AZStd::string>().c_str());
}
}
AZ::BehaviorEBus* Utils::ConstructAndRegisterScriptEventBehaviorEBus(const ScriptEvents::ScriptEvent& definition)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
if (behaviorContext == nullptr)
{
return nullptr;
}
AZ::BehaviorEBus* bus = aznew AZ::BehaviorEBus();
bus->m_attributes.push_back(AZStd::make_pair(AZ::RuntimeEBusAttribute, aznew AZ::Edit::AttributeData<bool>(true)));
bus->m_name = definition.GetName();
AZ::Uuid busIdType = azrtti_typeid<void>();
bool addressRequired = definition.IsAddressRequired();
if (addressRequired)
{
busIdType = definition.GetAddressType();
}
BehaviorParameterFromType(busIdType, addressRequired, bus->m_idParam);
bus->m_createHandler = aznew DefaultBehaviorHandlerCreator(bus, behaviorContext, bus->m_name + "::CreateHandler");
bus->m_destroyHandler = aznew DefaultBehaviorHandlerDestroyer(bus, behaviorContext, bus->m_name + "::DestroyHandler");
for (auto& method : definition.GetMethods())
{
const AZStd::string& methodName = method.GetName();
// If the script event has a valid address type, then we create an event method
if (IsAddressableTypeWithError(busIdType).IsSuccess())
{
bus->m_events[methodName].m_event = aznew ScriptEventMethod(behaviorContext, definition, methodName);
}
// For all Script Events provide a Broadcast, using Broadcast will bypass the addressing mechanism.
bus->m_events[methodName].m_broadcast = aznew ScriptEventBroadcast(behaviorContext, definition, methodName);
}
behaviorContext->m_ebuses[bus->m_name] = bus;
return bus;
}
bool Utils::DestroyScriptEventBehaviorEBus(AZStd::string_view ebusName)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
if (behaviorContext == nullptr)
{
return false;
}
bool erasedBus = false;
auto behaviorEbusEntry = behaviorContext->m_ebuses.find(ebusName);
if (behaviorEbusEntry != behaviorContext->m_ebuses.end())
{
AZ::BehaviorEBus* bus = behaviorEbusEntry->second;
delete bus;
behaviorContext->m_ebuses.erase(behaviorEbusEntry);
erasedBus = true;
}
return erasedBus;
}
ScriptEvent::~ScriptEvent()
{
for (auto ebusPair : m_behaviorEBus)
{
Utils::DestroyScriptEventBehaviorEBus(ebusPair.second->m_name);
}
m_scriptEventBindings.clear();
}
void ScriptEvent::Init(AZ::Data::AssetId scriptEventAssetId)
{
AZ_Assert(scriptEventAssetId.IsValid(), "Script Event requires a valid Asset Id");
m_assetId = scriptEventAssetId;
AZ::Data::AssetBus::Handler::BusConnect(scriptEventAssetId);
auto asset = AZ::Data::AssetManager::Instance().FindAsset<ScriptEvents::ScriptEventsAsset>(m_assetId, AZ::Data::AssetLoadBehavior::Default);
if (asset && asset.IsReady())
{
CompleteRegistration(asset);
}
}
void ScriptEvent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
CompleteRegistration(asset);
}
void ScriptEvent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
CompleteRegistration(asset);
}
void ScriptEvent::CompleteRegistration(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
m_assetId = asset.GetId();
const ScriptEvents::ScriptEvent& definition = asset.GetAs<ScriptEvents::ScriptEventsAsset>()->m_definition;
if (m_behaviorEBus.find(definition.GetVersion()) != m_behaviorEBus.end())
{
return;
}
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
AZ_Assert(behaviorContext, "Script Events require a valid Behavior Context");
m_busName = definition.GetName();
auto behaviorEbusEntry = behaviorContext->m_ebuses.find(definition.GetBehaviorContextName());
if (behaviorEbusEntry != behaviorContext->m_ebuses.end())
{
m_behaviorEBus[definition.GetVersion()] = behaviorEbusEntry->second;
if (m_maxVersion < definition.GetVersion())
{
m_maxVersion = definition.GetVersion();
}
m_scriptEventBindings[m_assetId] = AZStd::make_unique<ScriptEventBinding>(behaviorContext, m_busName.c_str(), definition.GetAddressType());
ScriptEventNotificationBus::Broadcast(&ScriptEventNotifications::OnRegistered, definition);
return;
}
AZ::BehaviorEBus* bus = Utils::ConstructAndRegisterScriptEventBehaviorEBus(definition);
if (bus == nullptr)
{
return;
}
m_behaviorEBus[definition.GetVersion()] = bus;
if (m_maxVersion < definition.GetVersion())
{
m_maxVersion = definition.GetVersion();
}
AZ::BehaviorContextBus::Broadcast(&AZ::BehaviorContextBus::Events::OnAddEBus, m_busName.c_str(), bus);
m_scriptEventBindings[m_assetId] = AZStd::make_unique<ScriptEventBinding>(behaviorContext, m_busName.c_str(), definition.GetAddressType());
ScriptEventNotificationBus::Event(m_assetId, &ScriptEventNotifications::OnRegistered, definition);
asset.Release();
m_isReady = true;
}
bool ScriptEvent::GetMethod(AZStd::string_view eventName, AZ::BehaviorMethod*& outMethod)
{
AZ::BehaviorEBus* ebus = GetBehaviorBus();
AZ_Assert(ebus, "BehaviorEBus is invalid: %s", m_busName.c_str());
const auto& method = ebus->m_events.find(eventName);
if (method == ebus->m_events.end())
{
AZ_Error("Script Events", false, "No method by name of %s found in the script event: %s", eventName.data(), m_busName.c_str());
return false;
}
AZ::EBusAddressPolicy addressPolicy
= (ebus->m_idParam.m_typeId.IsNull() || ebus->m_idParam.m_typeId == AZ::AzTypeInfo<void>::Uuid())
? AZ::EBusAddressPolicy::Single
: AZ::EBusAddressPolicy::ById;
AZ::BehaviorMethod* behaviorMethod
= ebus->m_queueFunction
? (addressPolicy == AZ::EBusAddressPolicy::ById ? method->second.m_queueEvent : method->second.m_queueBroadcast)
: (addressPolicy == AZ::EBusAddressPolicy::ById ? method->second.m_event : method->second.m_broadcast);
if (!behaviorMethod)
{
AZ_Error("Script Canvas", false, "Queue function mismatch in %s-%s", eventName.data(), m_busName.c_str());
return false;
}
outMethod = behaviorMethod;
return true;
}
}
}
@@ -0,0 +1,119 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/BehaviorContextFactoryMethods.h>
#include <ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
namespace ScriptEvents
{
class Parameter;
namespace Internal
{
class Utils
{
public:
static void BehaviorParameterFromType(AZ::Uuid typeId, bool addressable, AZ::BehaviorParameter& outParameter);
static void BehaviorParameterFromParameter(AZ::BehaviorContext* behaviorContext, const Parameter& parameter, const char* name, AZ::BehaviorParameter& outParameter);
static AZ::BehaviorEBus* ConstructAndRegisterScriptEventBehaviorEBus(const ScriptEvents::ScriptEvent& definition);
static bool DestroyScriptEventBehaviorEBus(AZStd::string_view ebusName);
};
//! This is the internal object that represents a ScriptEvent.
//! It provides the binding between the BehaviorContext and the messaging functionality.
//! It is ref counted so that it remains alive as long as anything is referencing it, this can happen
//! when multiple scripts or script canvas graphs are sending or receiving events defined in a given script event.
class ScriptEvent
: public AZ::Data::AssetBus::Handler
{
public:
AZ_RTTI(ScriptEvent, "{B8801400-65CD-49D5-B797-58E56D705A0A}");
AZ_CLASS_ALLOCATOR(ScriptEvent, AZ::SystemAllocator, 0);
AZ::AttributeArray* m_currentAttributes;
ScriptEvent() = default;
virtual ~ScriptEvent();
ScriptEvent(AZ::Data::AssetId scriptEventAssetId)
{
Init(scriptEventAssetId);
}
void Init(AZ::Data::AssetId scriptEventAssetId);
bool GetMethod(AZStd::string_view eventName, AZ::BehaviorMethod*& outMethod);
AZ::BehaviorEBus* GetBehaviorBus(AZ::u32 version = std::numeric_limits<AZ::u32>::max())
{
if (version == std::numeric_limits<AZ::u32>::max())
{
return m_behaviorEBus[m_maxVersion];
}
return m_behaviorEBus[version];
}
void CompleteRegistration(AZ::Data::Asset<AZ::Data::AssetData> asset);
AZStd::string GetBusName() const
{
return m_busName;
}
bool IsReady() const { return m_isReady; }
private:
AZ::u32 m_maxVersion = 0;
AZ::Data::AssetId m_assetId;
AZStd::string m_busName;
AZStd::unordered_map<AZ::u32, AZ::BehaviorEBus*> m_behaviorEBus; // version, ebus
AZStd::unordered_map<AZ::Data::AssetId, AZStd::unique_ptr<ScriptEventBinding>> m_scriptEventBindings;
bool m_isReady = false;
// AZ::Data::AssetBus::Handler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//
// Reference count for intrusive_ptr
template<class T>
friend struct AZStd::IntrusivePtrCountPolicy;
mutable unsigned int m_refCount = 0;
AZ_FORCE_INLINE void add_ref() { ++m_refCount; }
AZ_FORCE_INLINE void release()
{
AZ_Assert(m_refCount > 0, "Reference count logic error, trying to release reference when there are none left.");
if (--m_refCount == 0)
{
AZ::Data::AssetBus::Handler::BusDisconnect();
delete this;
}
}
AZStd::string m_previousName;
int m_previousVersion;
};
}
}
@@ -0,0 +1,168 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventDefinition.h"
#include "ScriptEventsBus.h"
#include <AzCore/std/string/regex.h>
namespace ScriptEvents
{
void ScriptEvent::RegisterInternal()
{
// Register the bus with the system component
ScriptEventBus::Broadcast(&ScriptEventRequests::RegisterScriptEventFromDefinition, *this);
}
void ScriptEvent::Register(AZ::ScriptDataContext&)
{
RegisterInternal();
}
void ScriptEvent::Release(AZ::ScriptDataContext&)
{
}
void ScriptEvent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ScriptEvent>()
->Version(1)
->Field("m_version", &ScriptEvent::m_version)
->Field("m_name", &ScriptEvent::m_name)
->Field("m_category", &ScriptEvent::m_category)
->Field("m_tooltip", &ScriptEvent::m_tooltip)
->Field("m_addressType", &ScriptEvent::m_addressType)
->Field("m_methods", &ScriptEvent::m_methods)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<ScriptEvent>("Script Event Definition", "Data driven script event definition")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::ChildNameLabelOverride, &ScriptEvent::GetLabel)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEvent::m_name, "Name", "Name of the Script Event")
->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEvent::m_tooltip, "Tooltip", "The name of this Script Event")
->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEvent::m_category, "Category", "The category that the Event will be put into")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ScriptEvent::m_addressType, "Address Type", "If required, this defines the address type for this event")
->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidAddressTypes)
->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEvent::m_methods, "Events", "The list of events available.")
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ScriptEvent>("ScriptEvent")
->Constructor<AZ::ScriptDataContext&>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Method("AddMethod", &ScriptEvent::AddMethod)
->Method("Register", &ScriptEvent::Register)
->Property("Name", BehaviorValueProperty(&ScriptEvent::m_name))
->Property("AddressType", BehaviorValueProperty(&ScriptEvent::m_addressType))
->Property("Events", BehaviorValueProperty(&ScriptEvent::m_methods))
;
}
}
AZ::Outcome<bool, AZStd::string> ScriptEvent::Validate() const
{
const AZStd::string& name = GetName();
const AZ::Uuid& addressType = GetAddressType();
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
AZ_Assert(behaviorContext, "A valid Behavior Context is expected");
if (m_version == 0 && behaviorContext->m_ebuses.find(name.c_str()) != behaviorContext->m_ebuses.end())
{
// An EBus with the same name already is registered, this is not allowed.
return AZ::Failure(AZStd::string::format("A Script Event with the name \"%s\" already exist, consider renaming this Script Event as duplicate names are not supported", name.c_str()));
}
// Validate address type
if (!Types::ValidateAddressType(addressType))
{
return AZ::Failure(AZStd::string::format("The specified type %s is not valid as an address for Script Events: %s", addressType.ToString<AZStd::string>().c_str(), name.c_str()));
}
// Definition name cannot be empty
if (name.empty())
{
return AZ::Failure(AZStd::string("Event name cannot be empty"));
}
// Name cannot start with a number
if (isdigit(name.at(0)))
{
return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str()));
}
AZStd::smatch match;
// Ascii-only
AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]");
AZStd::regex_match(name, match, asciionly_regex);
if (!match.empty())
{
return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str()));
}
// No whitespace
AZStd::regex nowhitespace_regex("[^\\S]");
AZStd::regex_match(name, match, nowhitespace_regex);
if (!match.empty())
{
return AZ::Failure(AZStd::string::format("%s, invalid name, event names should not contain white space", name.c_str()));
}
// Conform to valid function names
AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*");
AZStd::regex_match(name, match, validate_regex);
if (match.empty())
{
return AZ::Failure(AZStd::string::format("%s, invalid name specified, event name must only have alpha numeric characters, may not start with a number and may not have white space", name.c_str()));
}
if (m_methods.empty())
{
return AZ::Failure(AZStd::string::format("Script Events (%s) must provide at least one event otherwise they are unusable, be sure to add an event before saving.", name.c_str()));
}
// Validate each method
AZStd::string methodName;
int methodIndex = 0;
for (const Method& method : m_methods)
{
auto outcome = method.Validate();
if (!outcome.IsSuccess())
{
return outcome;
}
if (method.GetName().compare(methodName) == 0)
{
return AZ::Failure(AZStd::string::format("Cannot have duplicate method names (%d: %s) make sure each method name is unique", methodIndex, methodName.c_str()));
}
methodName = method.GetName();
++methodIndex;
}
return AZ::Success(true);
}
}
@@ -0,0 +1,212 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <ScriptEvents/ScriptEventParameter.h>
#include <ScriptEvents/ScriptEventMethod.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
namespace ScriptEvents
{
//! Defines a Script Event.
//! This is the user-facing Script Event definition.
//! When users create Script Events from Lua or in the editor this is the data definition that
//! a Script Event Asset will serialize.
class ScriptEvent
{
public:
AZ_TYPE_INFO(ScriptEvent, "{10A08CD3-32C9-4E18-8039-4B8A8157918E}");
bool IsAddressRequired() const
{
AZ::Uuid id = GetAddressType();
return id != azrtti_typeid<void>() && id != AZ::Uuid::CreateNull();
}
ScriptEvent()
: m_version(1)
, m_name("Name")
, m_category("Category")
, m_tooltip("Tooltip")
, m_addressType("Address Type")
{
m_name.Set("EventName");
m_category.Set("Script Events");
m_tooltip.Set("");
m_addressType.Set(azrtti_typeid<void>());
}
ScriptEvent(AZ::ScriptDataContext& dc)
: ScriptEvent()
{
if (dc.GetNumArguments() > 0)
{
AZStd::string name;
if (dc.ReadArg(0, name))
{
m_name.Set(name);
}
// \todo align with ScriptEvents error reporting policy, if the there is an argument but it is not an aztypeid
if (dc.GetNumArguments() > 1 && dc.IsClass<AZ::Uuid>(1))
{
AZ::Uuid addressType;
if (dc.ReadArg(1, addressType))
{
m_addressType.Set(addressType);
}
else
{
m_addressType.Set(azrtti_typeid<void>());
}
}
}
}
void MakeBackup()
{
IncreaseVersion();
}
void AddMethod(AZ::ScriptDataContext& dc)
{
if (dc.GetNumArguments() > 0)
{
Method& method = NewMethod();
method.FromScript(dc);
dc.PushResult(method);
}
}
void RegisterInternal();
void Register(AZ::ScriptDataContext& dc);
void Release(AZ::ScriptDataContext& dc);
Method& NewMethod()
{
m_methods.emplace_back();
return m_methods.back();
}
bool FindMethod(const AZ::Crc32& eventId, Method& outMethod) const
{
for (auto method : m_methods)
{
if (method.GetEventId() == eventId)
{
outMethod = method;
return true;
}
}
return false;
}
bool FindMethod(const AZStd::string_view& name, Method& outMethod) const
{
outMethod = {};
for (auto& method : m_methods)
{
if (method.GetName().compare(name) == 0)
{
outMethod = method;
return true;
}
}
return false;
}
bool HasMethod(const AZ::Crc32& eventId) const
{
for (auto& method : m_methods)
{
if (method.GetEventId() == eventId)
{
return true;
}
}
return false;
}
static void Reflect(AZ::ReflectContext* context);
AZ::u32 GetVersion() const { return m_version; }
AZStd::string GetName() const { return m_name.Get<AZStd::string>() ? *m_name.Get<AZStd::string>() : ""; }
AZStd::string GetCategory() const { return m_category.Get<AZStd::string>() ? *m_category.Get<AZStd::string>() : ""; }
AZStd::string GetTooltip() const { return m_tooltip.Get<AZStd::string>() ? *m_tooltip.Get<AZStd::string>() : ""; }
AZ::Uuid GetAddressType() const { return m_addressType.Get<AZ::Uuid>() ? *m_addressType.Get<AZ::Uuid>() : AZ::Uuid::CreateNull(); }
AZStd::string GetBehaviorContextName() const { return CreateBehaviorContextName(GetVersion()); }
AZStd::string CreateBehaviorContextName([[maybe_unused]] AZ::u32 versionNumber) const { return AZStd::string::format("%s_%i", GetName().c_str(), GetVersion()); }
const AZStd::vector<Method>& GetMethods() const { return m_methods; }
AZStd::string_view GetLabel() const { return GetName(); }
void SetVersion(AZ::u32 version) { m_version = version; }
ScriptEventData::VersionedProperty& GetNameProperty() { return m_name; }
ScriptEventData::VersionedProperty& GetCategoryProperty() { return m_category; }
ScriptEventData::VersionedProperty& GetTooltipProperty() { return m_tooltip; }
ScriptEventData::VersionedProperty& GetAddressTypeProperty() { return m_addressType; }
const ScriptEventData::VersionedProperty& GetNameProperty() const { return m_name; }
const ScriptEventData::VersionedProperty& GetCategoryProperty() const { return m_category; }
const ScriptEventData::VersionedProperty& GetTooltipProperty() const { return m_tooltip; }
const ScriptEventData::VersionedProperty& GetAddressTypeProperty() const { return m_addressType; }
//! Validates that the asset data being stored is valid and supported.
AZ::Outcome<bool, AZStd::string> Validate() const;
void IncreaseVersion()
{
m_name.PreSave();
m_category.PreSave();
m_tooltip.PreSave();
m_addressType.PreSave();
for (Method& method : m_methods)
{
method.PreSave();
}
++m_version;
}
void Flatten()
{
m_name.Flatten();
m_category.Flatten();
m_tooltip.Flatten();
m_addressType.Flatten();
for (Method& method : m_methods)
{
method.Flatten();
}
}
private:
AZ::u32 m_version;
ScriptEventData::VersionedProperty m_name;
ScriptEventData::VersionedProperty m_category;
ScriptEventData::VersionedProperty m_tooltip;
ScriptEventData::VersionedProperty m_addressType;
AZStd::vector<Method> m_methods;
};
}
@@ -0,0 +1,60 @@
/*
* 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.
*
*/
#pragma once
namespace ScriptEvents
{
struct FundamentalTypes final
{
AZ_TYPE_INFO(FundamentalTypes, "{7BEB1932-2CE2-4786-8320-E71B4E35FCFF}");
using TypeData = AZStd::unordered_map<AZ::Uuid, AZStd::string>;
TypeData m_typeData;
FundamentalTypes()
{
m_typeData = {
{ azrtti_typeid<bool>(), "Boolean" },
{ azrtti_typeid<short>(), "Number" },
{ azrtti_typeid<AZ::s64>(), "Number" },
{ azrtti_typeid<long>(), "Number" },
{ azrtti_typeid<unsigned char>(), "Number" },
{ azrtti_typeid<unsigned short>(), "Number" },
{ azrtti_typeid<unsigned int>(), "Number" },
{ azrtti_typeid<int>(), "Number" },
{ azrtti_typeid<AZ::u64>(), "Number" },
{ azrtti_typeid<unsigned long>(), "Number" },
{ azrtti_typeid<float>(), "Number" },
{ azrtti_typeid<double>(), "Number" },
{ azrtti_typeid<AZStd::string>(), "String" },
{ azrtti_typeid<AZStd::string_view>(), "String" },
{ azrtti_typeid<const char*>(), "String" }
};
}
const char* FindFundamentalTypeName(const AZ::Uuid& typeId) const
{
auto seek = m_typeData.find(typeId);
const char* result = (seek != m_typeData.end()) ? seek->second.c_str() : "";
return result;
}
bool IsFundamentalType(const AZ::Uuid& uuid) const
{
bool result = m_typeData.find(uuid) != m_typeData.end();
return result;
}
};
}
@@ -0,0 +1,264 @@
/*
* 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.
*
*/
#pragma once
#include <ScriptEvents/ScriptEventParameter.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <ScriptEvents/ScriptEventTypes.h>
namespace ScriptEvents
{
//! Holds the versioned definition for each of a script events.
//! You can think of this as a function declaration with a name, a return type
//! and an optional list of parameters (see ScriptEventParameter).
class Method
{
public:
AZ_TYPE_INFO(Method, "{E034EA83-C798-413D-ACE8-4923C51CF4F7}");
Method()
: m_name("Name")
, m_tooltip("Tooltip")
, m_returnType("Return Type")
{
m_name.Set("MethodName");
m_tooltip.Set("");
m_returnType.Set(azrtti_typeid<void>());
}
Method(const Method& rhs)
{
m_name = rhs.m_name;
m_tooltip = rhs.m_tooltip;
m_returnType = rhs.m_returnType;
m_parameters = rhs.m_parameters;
}
Method& operator=(const Method& rhs)
{
if (this != &rhs)
{
m_name = rhs.m_name;
m_tooltip = rhs.m_tooltip;
m_returnType = rhs.m_returnType;
m_parameters.assign(rhs.m_parameters.begin(), rhs.m_parameters.end());
}
return *this;
}
Method(Method&& rhs)
{
m_name = AZStd::move(rhs.m_name);
m_tooltip = AZStd::move(rhs.m_tooltip);
m_returnType = AZStd::move(rhs.m_returnType);
m_parameters.swap(rhs.m_parameters);
}
Method(AZ::ScriptDataContext& dc)
{
FromScript(dc);
}
void FromScript(AZ::ScriptDataContext& dc)
{
if (dc.GetNumArguments() > 0)
{
AZStd::string name;
if (dc.IsString(0) && dc.ReadArg(0, name))
{
m_name.Set(name.c_str());
}
if (dc.GetNumArguments() > 1)
{
AZ::Uuid returnType;
if (dc.ReadArg(1, returnType))
{
m_returnType.Set(returnType);
}
}
}
//AZ_TracePrintf("Script Events", "Added Script Method: %s (return type: %s)\n", GetName().c_str(), m_returnType.IsEmpty() ? "none" : GetReturnType().ToString<AZStd::string>().c_str());
}
~Method()
{
m_parameters.clear();
}
void AddParameter(AZ::ScriptDataContext& dc)
{
Parameter& parameter = NewParameter();
parameter.FromScript(dc);
dc.PushResult(parameter);
}
bool IsValid() const;
Parameter& NewParameter()
{
m_parameters.emplace_back();
return m_parameters.back();
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Method>()
->Field("m_name", &Method::m_name)
->Field("m_tooltip", &Method::m_tooltip)
->Field("m_returnType", &Method::m_returnType)
->Field("m_parameters", &Method::m_parameters)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<Method>("Script Event", "A script event's definition")
->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_name, "Name", "The specified name for this event, represents a callable function (i.e. MyScriptEvent())")
->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_tooltip, "Tooltip", "A description of this event")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &Method::m_returnType, "Return value type", "the typeid of the return value, ex. AZ::type_info<int>::Uuid foo()")
->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidReturnTypes)
->DataElement(AZ::Edit::UIHandlers::Default, &Method::m_parameters, "Parameters", "A list of parameters for the EBus event, ex. void foo(Parameter1, Parameter2)")
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Method>("Method")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Method("AddParameter", &Method::AddParameter)
->Property("Name", BehaviorValueProperty(&Method::m_name))
->Property("ReturnType", BehaviorValueProperty(&Method::m_returnType))
->Property("Parameters", BehaviorValueProperty(&Method::m_parameters))
;
}
}
AZStd::string GetName() const { return m_name.Get<AZStd::string>() ? *m_name.Get<AZStd::string>() : ""; }
AZStd::string GetTooltip() const { return m_tooltip.Get<AZStd::string>() ? *m_tooltip.Get<AZStd::string>() : ""; }
const AZ::Uuid GetReturnType() const { return m_returnType.Get<AZ::Uuid>() ? *m_returnType.Get<AZ::Uuid>() : AZ::Uuid::CreateNull(); }
const AZStd::vector<Parameter>& GetParameters() const { return m_parameters; }
ScriptEventData::VersionedProperty& GetNameProperty() { return m_name; }
ScriptEventData::VersionedProperty& GetTooltipProperty() { return m_tooltip; }
ScriptEventData::VersionedProperty& GetReturnTypeProperty() { return m_returnType; }
const ScriptEventData::VersionedProperty& GetNameProperty() const { return m_name; }
const ScriptEventData::VersionedProperty& GetTooltipProperty() const { return m_tooltip; }
const ScriptEventData::VersionedProperty& GetReturnTypeProperty() const { return m_returnType; }
AZ::Crc32 GetEventId() const { return AZ::Crc32(GetNameProperty().GetId().ToString<AZStd::string>().c_str()); }
//! Validates that the asset data being stored is valid and supported.
AZ::Outcome<bool, AZStd::string> Validate() const
{
const AZStd::string name = GetName();
const AZ::Uuid returnType = GetReturnType();
// Validate address type
if (!Types::IsValidReturnType(returnType))
{
return AZ::Failure(AZStd::string::format("The specified type %s is not valid as return type for Script Event: %s", returnType.ToString<AZStd::string>().c_str(), name.c_str()));
}
// Definition name cannot be empty
if (name.empty())
{
return AZ::Failure(AZStd::string("Definition name cannot be empty"));
}
// Name cannot start with a number
if (isdigit(name.at(0)))
{
return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str()));
}
// Conform to valid function names
AZStd::smatch match;
// Ascii-only
AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]");
AZStd::regex_match(name, match, asciionly_regex);
if (!match.empty())
{
return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str()));
}
AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*");
AZStd::regex_match(name, match, validate_regex);
if (match.empty())
{
return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str()));
}
AZStd::string parameterName;
int parameterIndex = 0;
for (const Parameter& parameter : m_parameters)
{
auto outcome = parameter.Validate();
if (!outcome.IsSuccess())
{
return outcome;
}
if (parameter.GetName().compare(parameterName) == 0)
{
return AZ::Failure(AZStd::string::format("Cannot have duplicate parameter names (%d: %s) make sure each parameter name is unique", parameterIndex, parameterName.c_str()));
}
parameterName = parameter.GetName();
++parameterIndex;
}
return AZ::Success(true);
}
void PreSave()
{
m_name.PreSave();
m_tooltip.PreSave();
m_returnType.PreSave();
for (Parameter parameter : m_parameters)
{
parameter.PreSave();
}
}
void Flatten()
{
m_name.Flatten();
m_tooltip.Flatten();
m_returnType.Flatten();
for (Parameter& parameter : m_parameters)
{
parameter.Flatten();
}
}
private:
ScriptEventData::VersionedProperty m_name;
ScriptEventData::VersionedProperty m_tooltip;
ScriptEventData::VersionedProperty m_returnType;
AZStd::vector<Parameter> m_parameters;
};
}
@@ -0,0 +1,189 @@
/*
* 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.
*
*/
#pragma once
#include <ScriptEvents/Internal/VersionedProperty.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <ScriptEvents/ScriptEventTypes.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/string/regex.h>
namespace ScriptEvents
{
//! An event's parameter definition, see (ScriptEventMethod)
class Parameter
{
public:
AZ_TYPE_INFO(Parameter, "{0DA4809B-08A6-49DC-9024-F81645D97FAC}");
Parameter()
: m_name("Name")
, m_tooltip("Tooltip")
, m_type("Type")
{
m_tooltip.Set("");
m_name.Set("ParameterName");
m_type.Set(azrtti_typeid<bool>());
}
Parameter(const Parameter& rhs)
{
m_name = rhs.m_name;
m_tooltip = rhs.m_tooltip;
m_type = rhs.m_type;
}
Parameter(AZ::ScriptDataContext& dc)
{
FromScript(dc);
}
void FromScript(AZ::ScriptDataContext& dc)
{
if (dc.GetNumArguments() > 0)
{
AZStd::string name;
if (dc.ReadArg(0, name))
{
m_name.Set(name.c_str());
}
if (dc.GetNumArguments() > 1)
{
AZ::Uuid parameterType;
if (dc.ReadArg(1, parameterType))
{
m_type.Set(parameterType);
}
}
}
//AZ_TracePrintf("Script Events", "Added Parameter: %s (type: %s)\n", GetName().c_str(), GetType().ToString<AZStd::string>() .c_str());
}
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Parameter>()
->Field("m_name", &Parameter::m_name)
->Field("m_tooltip", &Parameter::m_tooltip)
->Field("m_type", &Parameter::m_type)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<Parameter>("A Script Event's method parameter", "A parameter to a Script Event's event definition")
->DataElement(AZ::Edit::UIHandlers::Default, &Parameter::m_name, "Name", "Name of the parameter, ex. void foo(int thisIsTheParameterName)")
->DataElement(AZ::Edit::UIHandlers::Default, &Parameter::m_tooltip, "Tooltip", "A description of this parameter")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &Parameter::m_type, "Type", "The typeid of the parameter, ex. void foo(AZ::type_info<int>::Uuid())")
->Attribute(AZ::Edit::Attributes::GenericValueList, &Types::GetValidParameterTypes)
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<Parameter>("Parameter")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Property("Name", BehaviorValueProperty(&Parameter::m_name))
->Property("Type", BehaviorValueProperty(&Parameter::m_type))
;
}
}
AZ::Outcome<bool, AZStd::string> Validate() const
{
const AZStd::string& name = GetName();
const AZ::Uuid* parameterType = m_type.Get<const AZ::Uuid>();
AZ_Assert(parameterType && !parameterType->IsNull(), "The Parameter type should not be null");
// Validate address type
if (!Types::IsValidParameterType(*parameterType))
{
return AZ::Failure(AZStd::string::format("The specified type %s is not valid as parameter type for Script Event: %s", (*parameterType).ToString<AZStd::string>().c_str(), name.c_str()));
}
// Definition name cannot be empty
if (name.empty())
{
return AZ::Failure(AZStd::string("Definition name cannot be empty"));
}
// Name cannot start with a number
if (isdigit(name.at(0)))
{
return AZ::Failure(AZStd::string::format("%s, names cannot start with a number", name.c_str()));
}
// Conform to valid function names
AZStd::smatch match;
// Ascii-only
AZStd::regex asciionly_regex("[^\x0A\x0D\x20-\x7E]");
AZStd::regex_match(name, match, asciionly_regex);
if (match.size() > 0)
{
return AZ::Failure(AZStd::string::format("%s, invalid name, names may only contain ASCII characters", name.c_str()));
}
// Function name syntax
AZStd::regex validate_regex("[_[:alpha:]][_[:alnum:]]*");
AZStd::regex_match(name, match, validate_regex);
if (match.size() == 0)
{
return AZ::Failure(AZStd::string::format("%s, invalid name specified", name.c_str()));
}
return AZ::Success(true);
}
AZStd::string GetName() const { return m_name.Get<AZStd::string>() ? *m_name.Get<AZStd::string>() : ""; }
AZStd::string GetTooltip() const { return m_tooltip.Get<AZStd::string>() ? *m_tooltip.Get<AZStd::string>() : ""; }
AZ::Uuid GetType() const { return m_type.Get<AZ::Uuid>() ? *m_type.Get<AZ::Uuid>() : AZ::Uuid::CreateNull(); }
ScriptEventData::VersionedProperty& GetNameProperty() { return m_name; }
ScriptEventData::VersionedProperty& GetTooltipProperty() { return m_tooltip; }
ScriptEventData::VersionedProperty& GetTypeProperty() { return m_type; }
const ScriptEventData::VersionedProperty& GetNameProperty() const { return m_name; }
const ScriptEventData::VersionedProperty& GetTooltipProperty() const { return m_tooltip; }
const ScriptEventData::VersionedProperty& GetTypeProperty() const { return m_type; }
void PreSave()
{
m_name.PreSave();
m_tooltip.PreSave();
m_type.PreSave();
}
void Flatten()
{
m_name.Flatten();
m_tooltip.Flatten();
m_type.Flatten();
}
private:
ScriptEventData::VersionedProperty m_name;
ScriptEventData::VersionedProperty m_tooltip;
ScriptEventData::VersionedProperty m_type;
};
}
@@ -0,0 +1,101 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventSystem.h"
#include "ScriptEventDefinition.h"
#include "ScriptEventsAsset.h"
namespace ScriptEvents
{
AZStd::intrusive_ptr<Internal::ScriptEvent> ScriptEventsSystemComponentImpl::RegisterScriptEvent(const AZ::Data::AssetId& assetId, [[maybe_unused]] AZ::u32 version)
{
AZ_Assert(assetId.IsValid(), "Unable to register Script Event with invalid asset Id");
if (!assetId.IsValid())
{
return nullptr;
}
ScriptEventKey key(assetId, 0);
if (m_scriptEvents.find(key) == m_scriptEvents.end())
{
m_scriptEvents[key] = AZStd::intrusive_ptr<ScriptEvents::Internal::ScriptEvent>(aznew ScriptEvents::Internal::ScriptEvent(assetId));
}
return m_scriptEvents[key];
}
void ScriptEventsSystemComponentImpl::RegisterScriptEventFromDefinition(const ScriptEvents::ScriptEvent& definition)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
const AZStd::string& busName = definition.GetName();
const AZ::Uuid& assetId = AZ::Uuid::CreateName(busName.c_str());
ScriptEventKey key(assetId, 0);
const auto& ebusIterator = behaviorContext->m_ebuses.find(busName);
if (ebusIterator != behaviorContext->m_ebuses.end() && m_scriptEvents.find(key) != m_scriptEvents.end())
{
// We have already registered this Script Event, so we don't need to do anything further
return;
}
if (m_scriptEvents.find(key) == m_scriptEvents.end())
{
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> assetData = AZ::Data::AssetManager::Instance().CreateAsset<ScriptEvents::ScriptEventsAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
// Install the definition that's coming from Lua
ScriptEvents::ScriptEventsAsset* scriptAsset = assetData.Get();
scriptAsset->m_definition = definition;
m_scriptEvents[key] = AZStd::intrusive_ptr<ScriptEvents::Internal::ScriptEvent>(aznew ScriptEvents::Internal::ScriptEvent(assetId));
m_scriptEvents[key]->CompleteRegistration(assetData);
}
}
void ScriptEventsSystemComponentImpl::UnregisterScriptEventFromDefinition(const ScriptEvents::ScriptEvent& definition)
{
const AZStd::string& busName = definition.GetName();
const AZ::Uuid& assetId = AZ::Uuid::CreateName(busName.c_str());
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> assetData = AZ::Data::AssetManager::Instance().FindAsset<ScriptEvents::ScriptEventsAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
if (assetData)
{
assetData.Release();
}
}
AZStd::intrusive_ptr<Internal::ScriptEvent> ScriptEventsSystemComponentImpl::GetScriptEvent(const AZ::Data::AssetId& assetId, [[maybe_unused]] AZ::u32 version)
{
ScriptEventKey key(assetId, 0);
if (m_scriptEvents.find(key) != m_scriptEvents.end())
{
return m_scriptEvents[key];
}
AZ_Warning("Script Events", false, "Script event with asset Id %s was not found (version %d)", assetId.ToString<AZStd::string>().c_str(), version);
return nullptr;
}
const ScriptEvents::FundamentalTypes* ScriptEventsSystemComponentImpl::GetFundamentalTypes()
{
return &m_fundamentalTypes;
}
}
@@ -0,0 +1,66 @@
/*
* 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.
*
*/
#pragma once
#include "ScriptEventsBus.h"
namespace ScriptEvents
{
class ScriptEventsSystemComponentImpl
: protected ScriptEventBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ScriptEventsSystemComponentImpl, AZ::SystemAllocator, 0);
ScriptEventsSystemComponentImpl()
{
ScriptEventBus::Handler::BusConnect();
}
~ScriptEventsSystemComponentImpl()
{
ScriptEventBus::Handler::BusDisconnect();
}
virtual void RegisterAssetHandler() = 0;
virtual void UnregisterAssetHandler() = 0;
////////////////////////////////////////////////////////////////////////
// ScriptEvents::ScriptEventBus::Handler
AZStd::intrusive_ptr<Internal::ScriptEvent> RegisterScriptEvent(const AZ::Data::AssetId& assetId, AZ::u32 version) override;
void RegisterScriptEventFromDefinition(const ScriptEvents::ScriptEvent& definition) override;
void UnregisterScriptEventFromDefinition(const ScriptEvents::ScriptEvent& definition) override;
AZStd::intrusive_ptr<Internal::ScriptEvent> GetScriptEvent(const AZ::Data::AssetId& assetId, AZ::u32 version) override;
const FundamentalTypes* GetFundamentalTypes() override;
////////////////////////////////////////////////////////////////////////
private:
// Script Event Assets
AZStd::unordered_map<ScriptEventKey, AZStd::intrusive_ptr<ScriptEvents::Internal::ScriptEvent>> m_scriptEvents;
FundamentalTypes m_fundamentalTypes;
};
class ScriptEventModuleConfigurationRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual ScriptEventsSystemComponentImpl* GetSystemComponentImpl() = 0;
};
using ScriptEventModuleConfigurationRequestBus = AZ::EBus< ScriptEventModuleConfigurationRequests>;
}
@@ -0,0 +1,337 @@
/*
* 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.
*
*/
#include <ScriptEvents/ScriptEventTypes.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/Quaternion.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/sort.h>
namespace ScriptEvents
{
namespace Types
{
namespace
{
void ParseForVersionedTypes(AZ::BehaviorClass* behaviorClass, AZ::Crc32 attributeId, VersionedTypes& typeTarget, AZ::BehaviorContext& behaviorContext)
{
if (auto attributeAttribute = behaviorClass->FindAttribute(attributeId))
{
AZ::AttributeReader reader(behaviorClass, attributeAttribute);
bool isEnabled = false;
if (reader.Read<bool>(isEnabled))
{
if (isEnabled)
{
AZStd::string displayName = behaviorClass->m_name;
auto prettyNameAttribute = behaviorClass->FindAttribute(AZ::ScriptCanvasAttributes::PrettyName);
if (prettyNameAttribute != nullptr)
{
AZ::AttributeReader prettyNameReader(behaviorClass, prettyNameAttribute);
if (!prettyNameReader.Read<AZStd::string>(displayName, behaviorContext))
{
displayName = behaviorClass->m_name;
}
}
typeTarget.push_back(AZStd::make_pair(ScriptEventData::VersionedProperty::Make<AZ::Uuid>(behaviorClass->m_typeId, displayName.c_str()), displayName));
}
}
}
}
void ParseForTypeId(AZ::BehaviorClass* behaviorClass, AZ::Crc32 attributeId, AZStd::vector<AZ::Uuid>& uuidList)
{
if (auto paramAttribute = behaviorClass->FindAttribute(attributeId))
{
AZ::AttributeReader reader(behaviorClass, paramAttribute);
bool isEnabled = false;
if (reader.Read<bool>(isEnabled))
{
if (isEnabled)
{
uuidList.push_back(behaviorClass->m_typeId);
}
}
}
}
}
VersionedTypes GetValidAddressTypes()
{
using namespace ScriptEventData;
static VersionedTypes validAddressTypes;
if (validAddressTypes.empty())
{
validAddressTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<void>(), "None"), "None"));
validAddressTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZStd::string>(), "String"), "String"));
validAddressTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::EntityId>(), "Entity Id"), "Entity Id"));
validAddressTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Crc32>(), "Tag"), "Tag"));
}
return validAddressTypes;
}
// Returns the list of the valid Script Event method parameter type Ids, this is used
AZStd::vector<AZ::Uuid> GetSupportedParameterTypes()
{
static AZStd::vector<AZ::Uuid> supportedTypes;
if (supportedTypes.empty())
{
supportedTypes.push_back(azrtti_typeid<bool>());
supportedTypes.push_back(azrtti_typeid<double>());
supportedTypes.push_back(azrtti_typeid<AZ::EntityId>());
supportedTypes.push_back(azrtti_typeid<AZStd::string>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector2>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector3>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector4>());
supportedTypes.push_back(azrtti_typeid<AZ::Matrix3x3>());
supportedTypes.push_back(azrtti_typeid<AZ::Matrix4x4>());
supportedTypes.push_back(azrtti_typeid<AZ::Transform>());
supportedTypes.push_back(azrtti_typeid<AZ::Quaternion>());
supportedTypes.push_back(azrtti_typeid<AZ::Crc32>());
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
for (auto behaviorClassPair : behaviorContext->m_classes)
{
ParseForTypeId(behaviorClassPair.second, AZ::Script::Attributes::EnableAsScriptEventParamType, supportedTypes);
}
}
}
return supportedTypes;
}
// Returns the list of the valid Script Event method parameters, this is used to populate the ReflectedPropertyEditor's combobox
VersionedTypes GetValidParameterTypes()
{
using namespace ScriptEventData;
static VersionedTypes validParamTypes;
if (validParamTypes.empty())
{
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<bool>(), "Boolean"), "Boolean"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<double>(), "Number"), "Number"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZStd::string>(), "String"), "String"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::EntityId>(), "Entity Id"), "Entity Id"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector2>(), "Vector2"), "Vector2"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector3>(), "Vector3"), "Vector3"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector4>(), "Vector4"), "Vector4"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Matrix3x3>(), "Matrix3x3"), "Matrix3x3"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Matrix4x4>(), "Matrix4x4"), "Matrix4x4"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Transform>(), "Transform"), "Transform"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Quaternion>(), "Quaternion"), "Quaternion"));
validParamTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Crc32>(), "Tag"), "Tag"));
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
for (auto behaviorClassPair : behaviorContext->m_classes)
{
ParseForVersionedTypes(behaviorClassPair.second, AZ::Script::Attributes::EnableAsScriptEventParamType, validParamTypes, (*behaviorContext));
}
}
}
return validParamTypes;
}
// Determines whether the specified type is a valid parameter on a Script Event method argument list
bool IsValidParameterType(const AZ::Uuid& typeId)
{
for (const auto& supportedParam : GetSupportedParameterTypes())
{
if (supportedParam == typeId)
{
return true;
}
}
return false;
}
// Supported return types for Script Event methods
AZStd::vector<AZ::Uuid> GetSupportedReturnTypes()
{
static AZStd::vector<AZ::Uuid> supportedTypes;
if (supportedTypes.empty())
{
supportedTypes.push_back(azrtti_typeid<void>());
supportedTypes.push_back(azrtti_typeid<bool>());
supportedTypes.push_back(azrtti_typeid<double>());
supportedTypes.push_back(azrtti_typeid<AZ::EntityId>());
supportedTypes.push_back(azrtti_typeid<AZStd::string>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector2>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector3>());
supportedTypes.push_back(azrtti_typeid<AZ::Vector4>());
supportedTypes.push_back(azrtti_typeid<AZ::Matrix3x3>());
supportedTypes.push_back(azrtti_typeid<AZ::Matrix4x4>());
supportedTypes.push_back(azrtti_typeid<AZ::Transform>());
supportedTypes.push_back(azrtti_typeid<AZ::Quaternion>());
supportedTypes.push_back(azrtti_typeid<AZ::Crc32>());
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
for (auto behaviorClassPair : behaviorContext->m_classes)
{
ParseForTypeId(behaviorClassPair.second, AZ::Script::Attributes::EnableAsScriptEventReturnType, supportedTypes);
}
}
}
return supportedTypes;
}
VersionedTypes GetValidReturnTypes()
{
using namespace ScriptEventData;
static VersionedTypes validReturnTypes;
if (validReturnTypes.empty())
{
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<void>(), "None"), "None"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<bool>(), "Boolean"), "Boolean"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<double>(), "Number"), "Number"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZStd::string>(), "String"), "String"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::EntityId>(), "Entity Id"), "Entity Id"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector2>(), "Vector2"), "Vector2"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector3>(), "Vector3"), "Vector3"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Vector4>(), "Vector4"), "Vector4"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Matrix3x3>(), "Matrix3x3"), "Matrix3x3"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Matrix4x4>(), "Matrix4x4"), "Matrix4x4"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Transform>(), "Transform"), "Transform"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Quaternion>(), "Quaternion"), "Quaternion"));
validReturnTypes.push_back(AZStd::make_pair(VersionedProperty::Make<AZ::Uuid>(azrtti_typeid<AZ::Crc32>(), "Tag"), "Tag"));
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
for (auto behaviorClassPair : behaviorContext->m_classes)
{
ParseForVersionedTypes(behaviorClassPair.second, AZ::Script::Attributes::EnableAsScriptEventReturnType, validReturnTypes, (*behaviorContext));
}
}
}
return validReturnTypes;
}
bool IsValidReturnType(const AZ::Uuid& typeId)
{
for (const auto& supportedType: GetSupportedReturnTypes())
{
if (supportedType == typeId)
{
return true;
}
}
return false;
}
AZ::BehaviorMethod* FindBehaviorOperatorMethod(const AZ::BehaviorClass* behaviorClass, AZ::Script::Attributes::OperatorType operatorType)
{
AZ_Assert(behaviorClass, "Invalid AZ::BehaviorClass submitted to FindBehaviorOperatorMethod");
for (auto&& equalMethodCandidatePair : behaviorClass->m_methods)
{
const AZ::AttributeArray& attributes = equalMethodCandidatePair.second->m_attributes;
for (auto&& attributePair : attributes)
{
if (attributePair.second->RTTI_IsTypeOf(AZ::AzTypeInfo<AZ::AttributeData<AZ::Script::Attributes::OperatorType>>::Uuid()))
{
auto&& attributeData = AZ::RttiCast<AZ::AttributeData<AZ::Script::Attributes::OperatorType>*>(attributePair.second);
if (attributeData->Get(nullptr) == operatorType)
{
return equalMethodCandidatePair.second;
}
}
}
}
return nullptr;
}
bool IsAddressableType(const AZ::Uuid& uuid)
{
return !uuid.IsNull() && !AZ::BehaviorContext::IsVoidType(uuid);
}
bool ValidateAddressType(const AZ::Uuid& addressTypeId)
{
bool isValid = true;
if (IsAddressableType(addressTypeId))
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationBus::Events::GetBehaviorContext);
if (auto&& behaviorClass = behaviorContext->m_typeToClassMap[addressTypeId])
{
if (!behaviorClass->m_valueHasher)
{
AZ_Warning("Script Events", false, AZStd::string::format("Class %s with id %s must have an AZStd::hash<T> specialization to be a bus id", behaviorClass->m_name.c_str(), addressTypeId.ToString<AZStd::string>().c_str()).c_str());
return false;
}
if (!FindBehaviorOperatorMethod(behaviorClass, AZ::Script::Attributes::OperatorType::Equal) && !behaviorClass->m_equalityComparer)
{
AZ_Warning("Script Events", false, AZStd::string::format("Class %s with id %s does not have an operator equal defined to be a bus id", behaviorClass->m_name.c_str(), addressTypeId.ToString<AZStd::string>().c_str()).c_str());
return false;
}
}
else
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (auto&& classData = serializeContext->FindClassData(addressTypeId))
{
AZ_Warning("Script Events", false, AZStd::string::format("Type %s with id %s not found in behavior context", classData->m_name, addressTypeId.ToString<AZStd::string>().c_str()).c_str());
return false;
}
else
{
AZ_Warning("Script Events", false, AZStd::string::format("Type with id %s not found in behavior context", addressTypeId.ToString<AZStd::string>().c_str()).c_str());
return false;
}
}
}
return true;
}
}
}
@@ -0,0 +1,48 @@
/*
* 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.
*
*/
#pragma once
#include <ScriptEvents/Internal/VersionedProperty.h>
namespace ScriptEvents
{
namespace Types
{
using SupportedTypes = AZStd::vector< AZStd::pair< AZ::Uuid, AZStd::string> >;
using VersionedTypes = AZStd::vector<AZStd::pair< ScriptEventData::VersionedProperty, AZStd::string>>;
VersionedTypes GetValidAddressTypes();
// Returns the list of the valid Script Event method parameter type Ids, this is used
AZStd::vector<AZ::Uuid> GetSupportedParameterTypes();
// Returns the list of the valid Script Event method parameters, this is used to populate the ReflectedPropertyEditor's combobox
VersionedTypes GetValidParameterTypes();
// Determines whether the specified type is a valid parameter on a Script Event method argument list
bool IsValidParameterType(const AZ::Uuid& typeId);
// Supported return types for Script Event methods
AZStd::vector<AZ::Uuid> GetSupportedReturnTypes();
VersionedTypes GetValidReturnTypes();
bool IsValidReturnType(const AZ::Uuid& typeId);
AZ::BehaviorMethod* FindBehaviorOperatorMethod(const AZ::BehaviorClass* behaviorClass, AZ::Script::Attributes::OperatorType operatorType);
bool IsAddressableType(const AZ::Uuid& uuid);
bool ValidateAddressType(const AZ::Uuid& addressTypeId);
}
}
@@ -0,0 +1,155 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Asset/SimpleAsset.h>
#include "ScriptEventDefinition.h"
#include <AzFramework/Asset/GenericAssetHandler.h>
#include "ScriptEventsBus.h"
#include "ScriptEvent.h"
namespace ScriptEvents
{
class ScriptEventsAsset
: public AZ::Data::AssetData
{
public:
AZ_RTTI(ScriptEventsAsset, "{CB4D603E-8CB0-4D80-8165-4244F28AF187}", AZ::Data::AssetData);
AZ_CLASS_ALLOCATOR(ScriptEventsAsset, AZ::SystemAllocator, 0);
ScriptEventsAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId(), AZ::Data::AssetData::AssetStatus status = AZ::Data::AssetData::AssetStatus::NotLoaded)
: AZ::Data::AssetData(assetId, status)
{
}
ScriptEventsAsset(const ScriptEventsAsset& rhs)
{
m_definition = rhs.m_definition;
}
~ScriptEventsAsset() = default;
static const char* GetDisplayName() { return "Script Events"; }
static const char* GetGroup() { return "ScriptEvents"; }
static const char* GetFileFilter() { return "*.scriptevents"; }
static const char* GetFileExtension() { return ".scriptevents"; }
AZ::Crc32 GetBusId() const { return AZ::Crc32(GetId().ToString<AZStd::string>().c_str()); }
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ScriptEventsAsset>()
->Version(1)
->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true)
->Field("m_definition", &ScriptEventsAsset::m_definition)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<ScriptEventsAsset>("Script Events Asset", "")
->DataElement(0, &ScriptEventsAsset::m_definition, "Definition", "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
}
ScriptEvents::ScriptEvent m_definition;
};
class ScriptEventsAssetPtr
: public AZ::Data::Asset<ScriptEventsAsset>
{
using BaseType = AZ::Data::Asset<ScriptEventsAsset>;
public:
AZ_RTTI(ScriptEventsAssetPtr, "{CE2C30CB-709B-4BC0-BAEE-3D192D33367D}", BaseType);
AZ_CLASS_ALLOCATOR(ScriptEventsAssetPtr, AZ::SystemAllocator, 0);
ScriptEventsAssetPtr(AZ::Data::AssetLoadBehavior loadBehavior = AZ::Data::AssetLoadBehavior::PreLoad)
: AZ::Data::Asset<ScriptEventsAsset>(loadBehavior)
{}
ScriptEventsAssetPtr(const BaseType& scriptEventsAsset)
: BaseType(scriptEventsAsset)
{}
virtual ~ScriptEventsAssetPtr() = default;
using BaseType::BaseType;
using BaseType::operator=;
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext * serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ScriptEventsAssetPtr>()
;
}
}
};
// This is the Script Event asset handler used by the builder (and at runtime)
class ScriptEventAssetRuntimeHandler : public AzFramework::GenericAssetHandler<ScriptEventsAsset>
{
public:
AZ_RTTI(ScriptEventAssetRuntimeHandler, "{002E913D-339A-4238-BCCD-ED077BBD72C5}", AzFramework::GenericAssetHandler<ScriptEventsAsset>);
ScriptEventAssetRuntimeHandler(const char* displayName, const char* group, const char* extension, const AZ::Uuid& componentTypeId = AZ::Uuid::CreateNull(), AZ::SerializeContext* serializeContext = nullptr)
: AzFramework::GenericAssetHandler<ScriptEventsAsset>(displayName, group, extension, componentTypeId, serializeContext)
{
}
using AzFramework::GenericAssetHandler<ScriptEventsAsset>::LoadAssetData;
AZ::Data::AssetHandler::LoadResult LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
{
if (AzFramework::GenericAssetHandler<ScriptEventsAsset>::LoadAssetData(asset, stream, assetLoadFilterCB) ==
AZ::Data::AssetHandler::LoadResult::LoadComplete)
{
// Queue the registration against the AssetBus to be run on the main thread
// This avoids the following deadlock situation by moving RegisterScriptEvent to the main thread only:
// JobThread: LoadAssetData -> lock ScriptEventBus -> lock AssetBus
// MainThread: lock AssetBus OnAssetReady -> LoadAssetData -> ...
AZ::Data::AssetBus::QueueFunction([asset]()
{
const ScriptEvents::ScriptEvent& definition = asset.GetAs<ScriptEventsAsset>()->m_definition;
AZStd::intrusive_ptr<Internal::ScriptEvent> scriptEvent;
ScriptEvents::ScriptEventBus::BroadcastResult(scriptEvent, &ScriptEvents::ScriptEventRequests::RegisterScriptEvent, asset.GetId(), definition.GetVersion());
});
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
return AZ::Data::AssetHandler::LoadResult::Error;
}
};
}
@@ -0,0 +1,214 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/Script/ScriptContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Asset/SimpleAsset.h>
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventsAsset.h>
namespace ScriptEvents
{
class ScriptEventsAsset;
/**
* Provides script bindings to expose Script Event assets as script property.
*/
class ScriptEventsAssetRef
: private AZ::Data::AssetBus::Handler
{
public:
AZ_RTTI(ScriptEventsAssetRef, "{9BF12D72-9FE5-4F0E-A115-B92D99FB1CD7}");
AZ_CLASS_ALLOCATOR(ScriptEventsAssetRef, AZ::SystemAllocator, 0);
using AssetChangedCB = AZStd::function<void(const AZ::Data::Asset<ScriptEventsAsset>&, void* userData)>;
static void Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ScriptEventsAssetRef>()
->Version(0)
->Field("Asset", &ScriptEventsAssetRef::m_asset)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<ScriptEventsAssetRef>("Script Event Asset", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->DataElement(AZ::Edit::UIHandlers::Default, &ScriptEventsAssetRef::m_asset, "Script Event Asset", "")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEventsAssetRef::OnAssetChanged)
//TODO #lsempe: hook up to open Asset Editor when ready
//->Attribute("EditButton", "")
//->Attribute("EditDescription", "Open in Script Canvas Editor")
//->Attribute("EditCallback", &ScriptEventsAssetRef::LaunchScriptCanvasEditor)
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ScriptEventsAssetRef>()
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ConstructibleFromNil, false)
->Method("Get", &ScriptEventsAssetRef::GetDefinition)
;
}
}
ScriptEventsAssetRef() = default;
ScriptEventsAssetRef(AZ::Data::Asset<ScriptEventsAsset> asset, const AssetChangedCB& assetChangedCB, void* userData)
: m_asset(asset)
, m_assetNotifyCallback(assetChangedCB)
, m_userData(userData)
{
SetAsset(asset);
}
~ScriptEventsAssetRef()
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
const ScriptEvents::ScriptEvent* GetDefinition() const
{
if (ScriptEventsAsset* ebusAsset = m_asset.GetAs<ScriptEventsAsset>())
{
ebusAsset->m_definition.RegisterInternal();
return &ebusAsset->m_definition;
}
return nullptr;
}
void SetAsset(const AZ::Data::Asset<ScriptEventsAsset>& asset)
{
m_asset = asset;
if (m_asset.IsReady())
{
if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs<ScriptEventsAsset>())
{
scriptEventAsset->m_definition.RegisterInternal();
}
}
else
{
if (AZ::Data::AssetBus::Handler::BusIsConnectedId(m_asset.GetId()))
{
AZ::Data::AssetBus::Handler::BusDisconnect(m_asset.GetId());
}
AZ::Data::AssetBus::Handler::BusConnect(m_asset.GetId());
}
}
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> GetAsset() const
{
return m_asset;
}
void Load(bool loadBlocking /*= false*/)
{
if (!m_asset.IsReady())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_asset.GetId());
if (assetInfo.m_assetId.IsValid())
{
const AZ::Data::AssetType assetTypeId = azrtti_typeid<ScriptEventsAsset>();
auto& assetManager = AZ::Data::AssetManager::Instance();
m_asset = assetManager.GetAsset(m_asset.GetId(), azrtti_typeid<ScriptEventsAsset>(), m_asset.GetAutoLoadBehavior());
if(loadBlocking)
{
m_asset.BlockUntilLoadComplete();
}
}
}
}
AZ::u32 OnAssetChanged()
{
SetAsset(m_asset);
Load(false);
if (m_assetNotifyCallback)
{
m_assetNotifyCallback(m_asset, m_userData);
}
return AZ::Edit::PropertyRefreshLevels::None;
}
//=====================================================================
// AZ::Data::AssetBus
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override
{
if (ScriptEventsAsset* scriptEventAsset = m_asset.GetAs<ScriptEventsAsset>())
{
scriptEventAsset->m_definition.RegisterInternal();
}
}
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override
{
SetAsset(asset);
if (m_assetNotifyCallback)
{
m_assetNotifyCallback(m_asset, m_userData);
}
}
void OnAssetUnloaded([[maybe_unused]] const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetType assetType) override
{
if (ScriptEventsAsset* ebusAsset = m_asset.GetAs<ScriptEventsAsset>())
{
bool isRegistered = false;
//ScriptEventsLegacy::RegistrationRequestBus::BroadcastResult(isRegistered, &ScriptEventsLegacy::RegistrationRequestBus::Events::IsBusRegistered, ebusAsset->m_scriptEventsDefinition.m_name);
if (isRegistered)
{
//ScriptEventsLegacy::RegistrationRequestBus::Broadcast(&ScriptEventsLegacy::RegistrationRequestBus::Events::Unregister, ebusAsset->m_scriptEventsDefinition.m_name);
}
}
}
void OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, [[maybe_unused]] bool isSuccessful) override
{
SetAsset(m_asset);
}
//=====================================================================
private:
AssetChangedCB m_assetNotifyCallback;
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
void* m_userData;
};
}
@@ -0,0 +1,94 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/std/string/string.h>
#include <ScriptEvents/ScriptEvent.h>
#include <ScriptEvents/ScriptEventFundamentalTypes.h>
namespace ScriptEvents
{
class ScriptEvent;
class ScriptEventsHandler;
//! External facing API for registering and getting ScriptEvents
class ScriptEventRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
typedef AZStd::recursive_mutex MutexType;
virtual AZStd::intrusive_ptr<Internal::ScriptEvent> RegisterScriptEvent([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] AZ::u32 version) { return nullptr; }
virtual void RegisterScriptEventFromDefinition([[maybe_unused]] const ScriptEvent& definition) {}
virtual void UnregisterScriptEventFromDefinition([[maybe_unused]] const ScriptEvent& definition) {}
virtual AZStd::intrusive_ptr<Internal::ScriptEvent> GetScriptEvent([[maybe_unused]] const AZ::Data::AssetId& assetId, [[maybe_unused]] AZ::u32 version) { return {}; }
virtual const FundamentalTypes* GetFundamentalTypes() = 0;
};
using ScriptEventBus = AZ::EBus<ScriptEventRequests>;
//! Script event general purpose notifications
class ScriptEventNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::Data::AssetId;
virtual void OnRegistered(const ScriptEvent&) {}
};
using ScriptEventNotificationBus = AZ::EBus<ScriptEventNotifications>;
//! Used as the key into a map of ScriptEvents, it relies on the asset and version in order to support storing
//! multiple versions of a ScriptEvent definition.
struct ScriptEventKey
{
AZ::Data::AssetId m_assetId;
AZ::u32 m_version;
ScriptEventKey(AZ::Data::AssetId assetId, AZ::u32 version)
: m_assetId(assetId)
, m_version(version)
{}
bool operator==(const ScriptEventKey& rhs) const
{
return m_assetId == rhs.m_assetId && m_version == rhs.m_version;
}
};
}
namespace AZStd
{
// hash specialization
template <>
struct hash<ScriptEvents::ScriptEventKey>
{
typedef AZ::Uuid argument_type;
typedef size_t result_type;
size_t operator()(const ScriptEvents::ScriptEventKey& key) const
{
size_t retVal = 0;
AZStd::hash_combine(retVal, key.m_assetId);
AZStd::hash_combine(retVal, key.m_version);
return retVal;
}
};
}
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Module/Module.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Memory/Memory.h>
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventSystem.h>
namespace ScriptEvents
{
/**
* The ScriptEvents::Module class coordinates with the application
* to reflect classes and create system components.
*/
class ScriptEventsModule
: public AZ::Module
, ScriptEvents::ScriptEventModuleConfigurationRequestBus::Handler
{
public:
AZ_RTTI(ScriptEventsModule, "{DD54A1FE-2BDF-412C-AAB8-5A6BE01FE524}", AZ::Module);
AZ_CLASS_ALLOCATOR(ScriptEventsModule, AZ::SystemAllocator, 0);
ScriptEventsModule();
virtual ~ScriptEventsModule()
{
ScriptEvents::ScriptEventModuleConfigurationRequestBus::Handler::BusDisconnect();
delete m_systemImpl;
}
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
ScriptEventsSystemComponentImpl* GetSystemComponentImpl() override;
private:
ScriptEventsSystemComponentImpl* m_systemImpl;
};
}
@@ -0,0 +1,134 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/map.h>
namespace ScriptEventsLegacy
{
class IValidator
{
public:
virtual AZ::Outcome<bool, AZStd::string> Validate() = 0;
};
/**
* This class represents an EBus event parameter.
* void Foo(parameterType parameterName)
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* parameter
*/
struct ParameterDefinition
{
AZ_TYPE_INFO(ParameterDefinition, "{6586FFB5-0FF6-424F-A542-C797E2FF3458}");
AZ_CLASS_ALLOCATOR(ParameterDefinition, AZ::SystemAllocator, 0);
ParameterDefinition() = default;
ParameterDefinition(const AZStd::string& name, const AZStd::string& tooltip, const AZ::Uuid& type)
: m_name(name)
, m_tooltip(tooltip)
, m_type(type)
{}
AZStd::string m_name;
AZStd::string m_tooltip;
AZ::Uuid m_type = AZ::BehaviorContext::GetVoidTypeId();
};
/**
* This class represents an EBus event.
* void Foo (parameterType parameterName, parameterType2 parameterName2)
* ^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* m_returnType, m_name, m_parameters
*/
struct EventDefinition
{
AZ_TYPE_INFO(EventDefinition, "{211BB356-FA42-400F-B3DD-9326C6A686B6}");
AZ_CLASS_ALLOCATOR(EventDefinition, AZ::SystemAllocator, 0);
EventDefinition() = default;
EventDefinition(const AZStd::string& eventName, const AZStd::string& tooltip, const AZ::Uuid& returnValue, const AZStd::vector<ParameterDefinition>& parameters)
: m_name(eventName)
, m_tooltip(tooltip)
, m_returnType(returnValue)
, m_parameters(parameters)
{}
AZStd::string m_name;
AZStd::string m_tooltip;
AZ::Uuid m_returnType = AZ::BehaviorContext::GetVoidTypeId();
AZStd::vector<ParameterDefinition> m_parameters;
};
/**
* This class represents EBus type traits.
* At the moment only bus id type is supported.
*/
struct TypeTraitsDefinition
{
AZ_TYPE_INFO(TypeTraitsDefinition, "{EC374DE0-8003-4572-BC26-C4A8DBE50AB6}");
AZ_CLASS_ALLOCATOR(TypeTraitsDefinition, AZ::SystemAllocator, 0);
TypeTraitsDefinition() = default;
TypeTraitsDefinition(const AZ::Uuid& busIdType)
: m_busIdType(busIdType) {}
AZ::Uuid m_busIdType = AZ::BehaviorContext::GetVoidTypeId();
};
/**
* This class represents an EBus.
* An EBus has a name, traits, and a collection of events
* Configurable EBuses are added to the Behavior Context as both Request and Notification buses
*/
struct Definition
{
AZ_TYPE_INFO(Definition, "{4663215E-8137-4A16-979D-26B48401F40D}");
AZ_CLASS_ALLOCATOR(Definition, AZ::SystemAllocator, 0);
Definition() = default;
Definition(const AZStd::string& name, const AZStd::string& tooltip, const TypeTraitsDefinition& traits, const AZStd::vector<EventDefinition>& events)
: m_name(name)
, m_tooltip(tooltip)
, m_traits(traits)
, m_events(events)
{}
EventDefinition FindEvent(const char* name) const
{
for (const EventDefinition& eventDefinition : m_events)
{
if (eventDefinition.m_name.compare(name) == 0)
{
return eventDefinition;
}
}
return EventDefinition();
}
AZStd::string m_name;
AZStd::string m_tooltip;
AZStd::string m_category = "Custom Events";
TypeTraitsDefinition m_traits;
AZStd::vector<EventDefinition> m_events;
};
}
@@ -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.
*
*/
#include "precompiled.h"
#include <ScriptEvents/ScriptEventsGem.h>
#include <Source/Editor/ScriptEventsSystemEditorComponent.h>
#include <ScriptEvents/Components/ScriptEventReferencesComponent.h>
#include <Builder/ScriptEventsBuilderComponent.h>
#include <ScriptEvents/ScriptEventsBus.h>
#if defined(SCRIPTEVENTS_EDITOR)
namespace ScriptEvents
{
class ScriptEventsSystemComponentEditorImpl
: public ScriptEventsSystemComponentImpl
{
public:
~ScriptEventsSystemComponentEditorImpl() override
{
}
void RegisterAssetHandler() override
{
AZ::Data::AssetType assetType(azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
if (AZ::Data::AssetManager::Instance().GetHandler(assetType))
{
return; // Asset Type already handled
}
m_assetHandler = AZStd::make_unique<ScriptEventsEditor::ScriptEventAssetHandler>(
ScriptEvents::ScriptEventsAsset::GetDisplayName(),
ScriptEvents::ScriptEventsAsset::GetGroup(),
ScriptEvents::ScriptEventsAsset::GetFileExtension(),
AZ::AzTypeInfo<ScriptEventsEditor::ScriptEventEditorSystemComponent>::Uuid());
AZ::Data::AssetManager::Instance().RegisterHandler(m_assetHandler.get(), assetType);
// Use AssetCatalog service to register ScriptEvent asset type and extension
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddAssetType, assetType);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, assetType);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension,
ScriptEvents::ScriptEventsAsset::GetFileExtension());
}
void UnregisterAssetHandler() override
{
if (m_assetHandler)
{
AZ::Data::AssetManager::Instance().UnregisterHandler(m_assetHandler.get());
m_assetHandler.reset();
}
}
AZStd::unique_ptr<AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>> m_assetHandler;
};
ScriptEventsModule::ScriptEventsModule()
: AZ::Module()
, m_systemImpl(nullptr)
{
ScriptEvents::ScriptEventModuleConfigurationRequestBus::Handler::BusConnect();
m_descriptors.insert(m_descriptors.end(), {
ScriptEventsEditor::ScriptEventEditorSystemComponent::CreateDescriptor(),
ScriptEvents::Components::ScriptEventReferencesComponent::CreateDescriptor(),
ScriptEventsBuilder::ScriptEventsBuilderComponent::CreateDescriptor(),
});
}
ScriptEventsSystemComponentImpl* ScriptEventsModule::GetSystemComponentImpl()
{
if (!m_systemImpl)
{
m_systemImpl = aznew ScriptEventsSystemComponentEditorImpl();
}
return m_systemImpl;
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList ScriptEventsModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList{
azrtti_typeid<ScriptEventsEditor::ScriptEventEditorSystemComponent >(),
};
}
}
AZ_DECLARE_MODULE_CLASS(Gem_ScriptEvents, ScriptEvents::ScriptEventsModule)
#endif
@@ -0,0 +1,248 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventsSystemEditorComponent.h"
#include <ScriptEvents/Internal/VersionedProperty.h>
#include <AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.h>
#include <ScriptEvents/ScriptEventDefinition.h>
#include <ScriptEvents/ScriptEvent.h>
#include <AzCore/Component/TickBus.h>
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventSystem.h>
#if defined(SCRIPTEVENTS_EDITOR)
namespace ScriptEventsEditor
{
////////////////////////////
// ScriptEventAssetHandler
////////////////////////////
ScriptEventAssetHandler::ScriptEventAssetHandler(const char* displayName, const char* group, const char* extension, const AZ::Uuid& componentTypeId, AZ::SerializeContext* serializeContext)
: AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>(displayName, group, extension, componentTypeId, serializeContext)
{
}
AZ::Data::AssetPtr ScriptEventAssetHandler::CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type)
{
if (type != azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
{
return nullptr;
}
AZ::Data::AssetPtr assetPtr = AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>::CreateAsset(id, type);
if (!AzToolsFramework::AssetEditor::AssetEditorValidationRequestBus::MultiHandler::BusIsConnectedId(id))
{
AzToolsFramework::AssetEditor::AssetEditorValidationRequestBus::MultiHandler::BusConnect(id);
}
return assetPtr;
}
AZ::Data::AssetHandler::LoadResult ScriptEventAssetHandler::LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
AZ::Data::AssetHandler::LoadResult loadedData =
AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>::LoadAssetData(asset, stream, assetLoadFilterCB);
if (loadedData == AZ::Data::AssetHandler::LoadResult::LoadComplete)
{
ScriptEvents::ScriptEventsAsset* assetData = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (assetData)
{
auto busIter = m_previousEbusNames.find(asset.GetId());
bool registerBus = true;
if (busIter != m_previousEbusNames.end())
{
if (busIter->second.m_version < assetData->m_definition.GetVersion())
{
ScriptEvents::Internal::Utils::DestroyScriptEventBehaviorEBus(busIter->second.m_previousName);
m_previousEbusNames.erase(busIter);
}
else
{
registerBus = false;
}
}
if (registerBus)
{
// LoadAssetData is being called from an Asset system thread,
// we need to complete registering with the BehaviorContext in the main thread
auto registerBusFn = [this, assetData, asset]()
{
if (ScriptEvents::Internal::Utils::ConstructAndRegisterScriptEventBehaviorEBus(assetData->m_definition))
{
PreviousNameSettings previousSettings;
previousSettings.m_previousName = assetData->m_definition.GetName().c_str();
previousSettings.m_version = assetData->m_definition.GetVersion();
m_previousEbusNames[asset.GetId()] = previousSettings;
}
};
AZ::TickBus::QueueFunction(registerBusFn);
}
}
}
return loadedData;
}
bool ScriptEventAssetHandler::SaveAssetData(const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZ::IO::GenericStream* stream)
{
AZ_TracePrintf("ScriptEvent", "Trying to save Asset with ID: %s - SCRIPTEVENT", asset.Get()->GetId().ToString<AZStd::string>().c_str());
// Attempt to Save the data to a temporary stream in order to see if any
AZ::Outcome<bool, AZStd::string> outcome = AZ::Failure(AZStd::string::format("AssetEditorValidationRequests is not connected ID: %s", asset.Get()->GetId().ToString<AZStd::string>().c_str()));
// Verify that the asset is in a valid state that can be saved.
AzToolsFramework::AssetEditor::AssetEditorValidationRequestBus::EventResult(outcome, asset.Get()->GetId(), &AzToolsFramework::AssetEditor::AssetEditorValidationRequests::IsAssetDataValid, asset);
if (!outcome.IsSuccess())
{
AZ_Error("Asset Editor", false, "%s", outcome.GetError().c_str());
return false;
}
ScriptEvents::ScriptEventsAsset* assetData = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
AZ_Assert(assetData, "Asset is of the wrong type.");
if (assetData && m_serializeContext)
{
return AZ::Utils::SaveObjectToStream<ScriptEvents::ScriptEventsAsset>(*stream,
m_saveAsBinary ? AZ::ObjectStream::ST_BINARY : AZ::ObjectStream::ST_XML,
assetData,
m_serializeContext);
}
return false;
}
AZ::Outcome<bool, AZStd::string> ScriptEventAssetHandler::IsAssetDataValid(const AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
ScriptEvents::ScriptEventsAsset* assetData = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (!assetData)
{
return AZ::Failure(AZStd::string::format("Unable to validate asset with id: %s it has not been registered with the Script Event system component.", asset.GetId().ToString<AZStd::string>().c_str()));
}
const ScriptEvents::ScriptEvent* definition = &assetData->m_definition;
AZ_Assert(definition, "The AssetData should have a valid definition");
return definition->Validate();
}
void ScriptEventAssetHandler::PreAssetSave(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ScriptEvents::ScriptEventsAsset* scriptEventAsset = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
scriptEventAsset->m_definition.IncreaseVersion();
}
void ScriptEventAssetHandler::BeforePropertyEdit(AzToolsFramework::InstanceDataNode* node, AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ScriptEventData::VersionedProperty* property = nullptr;
AzToolsFramework::InstanceDataNode* parent = node;
while (parent)
{
if (parent->GetClassMetadata()->m_typeId == azrtti_typeid<ScriptEventData::VersionedProperty>())
{
property = static_cast<ScriptEventData::VersionedProperty*>(parent->GetInstance(0));
break;
}
parent = parent->GetParent();
}
if (property)
{
property->OnPropertyChange();
}
}
void ScriptEventEditorSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<ScriptEventEditorSystemComponent, AZ::Component>()
->Version(3)
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC("AssetBuilder", 0xc739c7d7) }));
;
}
using namespace ScriptEvents;
ScriptEventData::VersionedProperty::Reflect(context);
Parameter::Reflect(context);
Method::Reflect(context);
ScriptEvent::Reflect(context);
ScriptEventsAsset::Reflect(context);
ScriptEventsAssetRef::Reflect(context);
ScriptEventsAssetPtr::Reflect(context);
}
void ScriptEventEditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ScriptEventsService", 0x6897c23b));
}
void ScriptEventEditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ScriptEventsService", 0x6897c23b));
}
////////////////////
// SystemComponent
////////////////////
void ScriptEventEditorSystemComponent::Activate()
{
using namespace ScriptEvents;
ScriptEventsSystemComponentImpl* moduleConfiguration = nullptr;
ScriptEventModuleConfigurationRequestBus::BroadcastResult(moduleConfiguration, &ScriptEventModuleConfigurationRequests::GetSystemComponentImpl);
if (moduleConfiguration)
{
moduleConfiguration->RegisterAssetHandler();
}
m_propertyHandlers.emplace_back(AzToolsFramework::RegisterGenericComboBoxHandler<ScriptEventData::VersionedProperty>());
}
void ScriptEventEditorSystemComponent::Deactivate()
{
for (auto&& propertyHandler : m_propertyHandlers)
{
AzToolsFramework::PropertyTypeRegistrationMessages::Bus::Broadcast(&AzToolsFramework::PropertyTypeRegistrationMessages::UnregisterPropertyType, propertyHandler.get());
}
m_propertyHandlers.clear();
using namespace ScriptEvents;
ScriptEventsSystemComponentImpl* moduleConfiguration = nullptr;
ScriptEventModuleConfigurationRequestBus::BroadcastResult(moduleConfiguration, &ScriptEventModuleConfigurationRequests::GetSystemComponentImpl);
if (moduleConfiguration)
{
moduleConfiguration->UnregisterAssetHandler();
}
}
}
#endif
@@ -0,0 +1,100 @@
/*
* 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.
*
*/
#pragma once
#include "ScriptEventsSystemComponent.h"
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
#include <ScriptEvents/ScriptEventsAssetRef.h>
namespace AzToolsFramework { class PropertyHandlerBase; }
#if defined(SCRIPTEVENTS_EDITOR)
namespace ScriptEventsEditor
{
// This is the ScriptEvent asset handler used by the Asset Editor, it does additional validation that is not
// needed when saving the asset through the builder
class ScriptEventAssetHandler
: public AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>
, AzToolsFramework::AssetEditor::AssetEditorValidationRequestBus::MultiHandler
{
public:
AZ_RTTI(ScriptEventAssetHandler, "{D81DE7D5-5ED0-4D70-8364-AA986E9C490E}", AzFramework::GenericAssetHandler<ScriptEvents::ScriptEventsAsset>);
ScriptEventAssetHandler(const char* displayName, const char* group, const char* extension, const AZ::Uuid& componentTypeId = AZ::Uuid::CreateNull(), AZ::SerializeContext* serializeContext = nullptr);
AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
AZ::Data::AssetHandler::LoadResult LoadAssetData(
const AZ::Data::Asset<AZ::Data::AssetData>& asset,
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
bool SaveAssetData(const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZ::IO::GenericStream* stream) override;
// AssetEditorValidationRequestBus::Handler
AZ::Outcome<bool, AZStd::string> IsAssetDataValid(const AZ::Data::Asset<AZ::Data::AssetData>& asset) override;
void PreAssetSave(AZ::Data::Asset<AZ::Data::AssetData> asset);
void BeforePropertyEdit(AzToolsFramework::InstanceDataNode* node, AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void SetSaveAsBinary(bool saveAsBinary) { m_saveAsBinary = saveAsBinary; }
private:
struct PreviousNameSettings
{
AZStd::string m_previousName;
AZ::u32 m_version;
};
AZStd::unordered_map< AZ::Data::AssetId, PreviousNameSettings > m_previousEbusNames;
bool m_saveAsBinary;
};
class ScriptEventEditorSystemComponent
: public AZ::Component
{
public:
AZ_COMPONENT(ScriptEventEditorSystemComponent, "{8BAD5292-56C3-4657-99F2-515A2BDE23C1}");
ScriptEventEditorSystemComponent() = default;
ScriptEventEditorSystemComponent(const ScriptEventEditorSystemComponent&) = delete;
protected:
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override {}
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
AZStd::vector<AZStd::unique_ptr<AzToolsFramework::PropertyHandlerBase>> m_propertyHandlers;
// Script Event Assets
AZStd::unordered_map<ScriptEvents::ScriptEventKey, AZStd::intrusive_ptr<ScriptEvents::Internal::ScriptEvent>> m_scriptEvents;
};
}
#endif
@@ -0,0 +1,57 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventsSystemComponent.h"
#include <AzCore/Memory/SystemAllocator.h>
#include <ScriptEvents/ScriptEventsGem.h>
#include <ScriptEvents/Components/ScriptEventReferencesComponent.h>
namespace ScriptEvents
{
ScriptEventsModule::ScriptEventsModule()
: AZ::Module()
, m_systemImpl(nullptr)
{
ScriptEventModuleConfigurationRequestBus::Handler::BusConnect();
m_descriptors.insert(m_descriptors.end(), {
ScriptEvents::ScriptEventsSystemComponent::CreateDescriptor(),
ScriptEvents::Components::ScriptEventReferencesComponent::CreateDescriptor(),
});
}
ScriptEventsSystemComponentImpl* ScriptEventsModule::GetSystemComponentImpl()
{
if (!m_systemImpl)
{
m_systemImpl = aznew ScriptEventsSystemComponentRuntimeImpl();
}
return m_systemImpl;
}
/**
* Add required SystemComponents to the SystemEntity.
*/
AZ::ComponentTypeList ScriptEventsModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList{
azrtti_typeid<ScriptEvents::ScriptEventsSystemComponent>(),
};
}
}
AZ_DECLARE_MODULE_CLASS(Gem_ScriptEvents, ScriptEvents::ScriptEventsModule)
@@ -0,0 +1,138 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventsSystemComponent.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Color.h>
#include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/sort.h>
#include <AzFramework/Asset/GenericAssetHandler.h>
#include <ScriptEvents/ScriptEventsAssetRef.h>
#include <ScriptEvents/ScriptEventDefinition.h>
#include <ScriptEvents/ScriptEventFundamentalTypes.h>
namespace ScriptEvents
{
void ScriptEventsSystemComponent::Reflect(AZ::ReflectContext* context)
{
using namespace ScriptEvents;
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<ScriptEventsSystemComponent, AZ::Component>()
->Version(1)
// ScriptEvents avoids a use dependency on the AssetBuilderSDK. Therefore the Crc is used directly to register this component with the Gem builder
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC("AssetBuilder", 0xc739c7d7) }));
;
}
ScriptEventData::VersionedProperty::Reflect(context);
Parameter::Reflect(context);
Method::Reflect(context);
ScriptEvent::Reflect(context);
ScriptEvents::ScriptEventsAsset::Reflect(context);
ScriptEvents::ScriptEventsAssetRef::Reflect(context);
ScriptEvents::ScriptEventsAssetPtr::Reflect(context);
}
void ScriptEventsSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ScriptEventsService", 0x6897c23b));
}
void ScriptEventsSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ScriptEventsService", 0x6897c23b));
}
void ScriptEventsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("AssetDatabaseService", 0x3abf5601));
}
void ScriptEventsSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
AZ_UNUSED(dependent);
}
void ScriptEventsSystemComponent::Init()
{
}
void ScriptEventsSystemComponent::Activate()
{
ScriptEventsSystemComponentImpl* moduleConfiguration = nullptr;
ScriptEventModuleConfigurationRequestBus::BroadcastResult(moduleConfiguration, &ScriptEventModuleConfigurationRequests::GetSystemComponentImpl);
if (moduleConfiguration)
{
moduleConfiguration->RegisterAssetHandler();
}
}
void ScriptEventsSystemComponent::Deactivate()
{
for (auto& asset : m_scriptEvents)
{
asset.second.reset();
}
m_scriptEvents.clear();
ScriptEventsSystemComponentImpl* moduleConfiguration = nullptr;
ScriptEventModuleConfigurationRequestBus::BroadcastResult(moduleConfiguration, &ScriptEventModuleConfigurationRequests::GetSystemComponentImpl);
if (moduleConfiguration)
{
moduleConfiguration->UnregisterAssetHandler();
}
}
void ScriptEventsSystemComponentRuntimeImpl::RegisterAssetHandler()
{
AZ::Data::AssetType assetType(azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
if (AZ::Data::AssetManager::Instance().GetHandler(assetType))
{
return; // Asset Type already handled
}
m_assetHandler = AZStd::make_unique<ScriptEventAssetRuntimeHandler>(ScriptEvents::ScriptEventsAsset::GetDisplayName(),
ScriptEvents::ScriptEventsAsset::GetGroup(), ScriptEvents::ScriptEventsAsset::GetFileExtension(),
AZ::AzTypeInfo<ScriptEvents::ScriptEventsSystemComponent>::Uuid());
AZ::Data::AssetManager::Instance().RegisterHandler(m_assetHandler.get(), assetType);
// Use AssetCatalog service to register ScriptCanvas asset type and extension
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddAssetType, assetType);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, assetType);
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension,
ScriptEvents::ScriptEventsAsset::GetFileExtension());
}
void ScriptEventsSystemComponentRuntimeImpl::UnregisterAssetHandler()
{
AZ::Data::AssetManager::Instance().UnregisterHandler(m_assetHandler.get());
m_assetHandler.reset();
}
}
@@ -0,0 +1,71 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <ScriptEvents/ScriptEvent.h>
#include <ScriptEvents/ScriptEventsBus.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <ScriptEvents/ScriptEventDefinition.h>
#include <ScriptEvents/ScriptEventSystem.h>
namespace ScriptEvents
{
class ScriptEventsSystemComponent
: public AZ::Component
{
public:
AZ_COMPONENT(ScriptEventsSystemComponent, "{43068F27-B171-4DF4-B583-57CEF3F2AC6C}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
protected:
////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Init() override;
void Activate() override;
void Deactivate() override;
////////////////////////////////////////////////////////////////////////
// Script Event Assets
AZStd::unordered_map<ScriptEventKey, AZStd::intrusive_ptr<ScriptEvents::Internal::ScriptEvent>> m_scriptEvents;
};
class ScriptEventsSystemComponentRuntimeImpl
: public ScriptEventsSystemComponentImpl
{
public:
void RegisterAssetHandler() override;
void UnregisterAssetHandler() override;
AZStd::unique_ptr<ScriptEventAssetRuntimeHandler> m_assetHandler;
};
}
@@ -0,0 +1,13 @@
/*
* 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.
*
*/
#include "precompiled.h"
@@ -0,0 +1,16 @@
/*
* 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.
*
*/
#pragma once
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
@@ -0,0 +1,50 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include "ScriptEventTestUtilities.h"
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzTest/AzTest.h>
namespace ScriptEventsTests
{
namespace Utilities
{
void Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Method("ScriptExpectTrue", &ScriptExpectTrue);
behaviorContext->Method("ScriptTrace", &ScriptTrace);
}
}
void ScriptExpectTrue(bool check, const char* msg)
{
(void)check; (void)msg;
EXPECT_TRUE(check) << msg;
}
void ScriptTrace(const char* txt)
{
static bool showTraces = true;
if (showTraces)
{
std::cerr << txt << std::endl;
}
}
}
}
@@ -0,0 +1,29 @@
/*
* 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.
*
*/
#pragma once
namespace AZ
{
class ReflectContext;
}
namespace ScriptEventsTests
{
namespace Utilities
{
void Reflect(AZ::ReflectContext* context);
void ScriptExpectTrue(bool check, const char* msg);
void ScriptTrace(const char* txt);
}
}
@@ -0,0 +1,17 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include <AzTest/AzTest.h>
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);
@@ -0,0 +1,58 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright EntityRef 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.
*
*/
#pragma once
#include "ScriptEventsSystemComponent.h"
#include <AzFramework/Application/Application.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzFramework/Asset/AssetCatalogComponent.h>
namespace ScriptEventsTests
{
class Application
: public AzFramework::Application
{
public:
using SuperType = AzFramework::Application;
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
AZ::ComponentTypeList components;
components.insert(components.end(),
{
azrtti_typeid<ScriptEvents::ScriptEventsSystemComponent>(),
azrtti_typeid<AZ::MemoryComponent>(),
azrtti_typeid<AZ::AssetManagerComponent>(),
azrtti_typeid<AZ::JobManagerComponent>(),
azrtti_typeid<AZ::StreamerComponent>(),
azrtti_typeid<AzFramework::AssetCatalogComponent>(),
});
return components;
}
void CreateReflectionManager() override
{
SuperType::CreateReflectionManager();
RegisterComponentDescriptor(ScriptEvents::ScriptEventsSystemComponent::CreateDescriptor());
RegisterComponentDescriptor(AZ::MemoryComponent::CreateDescriptor());
RegisterComponentDescriptor(AZ::AssetManagerComponent::CreateDescriptor());
RegisterComponentDescriptor(AzFramework::AssetCatalogComponent::CreateDescriptor());
}
};
}
@@ -0,0 +1,28 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright EntityRef 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.
*
*/
#include "precompiled.h"
#include "ScriptEventsTestFixture.h"
#include "ScriptEventsTestApplication.h"
namespace ScriptEventsTests
{
ScriptEventsTests::Application* ScriptEventsTestFixture::s_application = nullptr;
UnitTest::AllocatorsBase ScriptEventsTestFixture::s_allocatorSetup = {};
ScriptEventsTests::Application* ScriptEventsTestFixture::GetApplication()
{
return s_application;
}
}
@@ -0,0 +1,135 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright EntityRef 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.
*
*/
#pragma once
#include "ScriptEventsTestApplication.h"
#include <AzTest/AzTest.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Jobs/JobManager.h>
#include <AzCore/Jobs/JobContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <ScriptEvents/ScriptEventsGem.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
#include <ScriptEvents/ScriptEventParameter.h>
#include <ScriptEvents/ScriptEventMethod.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include "ScriptEventTestUtilities.h"
#include <AzCore/Math/MathReflection.h>
#include <AzCore/Memory/PoolAllocator.h>
namespace ScriptEventsTests
{
class ScriptEventsTestFixture
: public ::testing::Test
{
static const bool s_enableMemoryLeakChecking;
static ScriptEventsTests::Application* GetApplication();
protected:
static ScriptEventsTests::Application* s_application;
static UnitTest::AllocatorsBase s_allocatorSetup;
static void SetUpTestCase()
{
s_allocatorSetup.SetupAllocator();
if (s_application == nullptr)
{
AZ::ComponentApplication::StartupParameters appStartup;
s_application = aznew ScriptEventsTests::Application();
{
AZ::ComponentApplication::Descriptor descriptor;
descriptor.m_enableDrilling = false; // We'll manage our own driller in these tests
descriptor.m_useExistingAllocator = true; // Use the SystemAllocator we own in this test.
appStartup.m_createStaticModulesCallback =
[](AZStd::vector<AZ::Module*>& modules)
{
modules.emplace_back(new ScriptEvents::ScriptEventsModule);
};
s_application->Start(descriptor, appStartup);
}
AZ::SerializeContext* serializeContext = s_application->GetSerializeContext();
serializeContext->RegisterGenericType<AZStd::string>();
serializeContext->RegisterGenericType<AZStd::any>();
AZ::BehaviorContext* behaviorContext = s_application->GetBehaviorContext();
Utilities::Reflect(behaviorContext);
AZ::Entity* systemEntity = s_application->FindEntity(AZ::SystemEntityId);
AZ_Assert(systemEntity, "SystemEntity must exist");
}
AZ::TickBus::AllowFunctionQueuing(true);
}
static void TearDownTestCase()
{
AZ::Entity* systemEntity = s_application->FindEntity(AZ::SystemEntityId);
AZ_Assert(systemEntity, "SystemEntity must exist");
AZ::Data::AssetManager::Instance().DispatchEvents();
if (s_application)
{
s_application->Stop();
delete s_application;
s_application = nullptr;
}
s_allocatorSetup.TeardownAllocator();
}
void SetUp() override
{
m_serializeContext = s_application->GetSerializeContext();
m_behaviorContext = s_application->GetBehaviorContext();
if (!AZ::IO::FileIOBase::GetInstance())
{
m_fileIO.reset(aznew AZ::IO::LocalFileIO());
AZ::IO::FileIOBase::SetInstance(m_fileIO.get());
}
AZ_Assert(AZ::IO::FileIOBase::GetInstance(), "File IO was not properly installed");
}
void TearDown() override
{
AZ::IO::FileIOBase::SetInstance(nullptr);
m_fileIO = nullptr;
}
AZStd::unique_ptr<AZ::IO::FileIOBase> m_fileIO;
AZ::SerializeContext* m_serializeContext;
AZ::BehaviorContext* m_behaviorContext;
};
}
@@ -0,0 +1,585 @@
/*
* 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.
*
*/
#include "precompiled.h"
#include <AzTest/AzTest.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Math/Uuid.h>
#include <ScriptEvents/Internal/VersionedProperty.h>
#include <ScriptEvents/ScriptEventDefinition.h>
#include <ScriptEvents/ScriptEvent.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <Internal/BehaviorContextBinding/ScriptEventMethod.h>
#include <Tests/ScriptEventsTestFixture.h>
#include <Tests/ScriptEventTestUtilities.h>
namespace ScriptEventsTests
{
//////////////////////////////////////////////////////////////////////////]
TEST_F(ScriptEventsTestFixture, ScriptEventRefactor_LuaScriptEventWithId)
{
// Tests Script Events that rely on addressable buses.
const char luaCode[] =
R"( luaScriptEventWithId = {
MethodWithId0 = function(self, param1, param2)
ScriptTrace("Handler: " .. tostring(param1) .. " " .. tostring(param2))
ScriptExpectTrue(typeid(param1) == typeid(0), "Type of param1 must be "..tostring(typeid(0)))
ScriptExpectTrue(typeid(param2) == typeid(EntityId()), "Type of param2 must be "..tostring(typeid(EntityId())))
ScriptExpectTrue(param1 == 1, "The first parameter must be 1")
ScriptExpectTrue(param2 == EntityId(12345), "The received entity Id must match the one sent")
ScriptTrace("MethodWithId0 handled")
return true
end,
MethodWithId1 = function(self)
ScriptTrace("MethodWithId1 handled")
end
}
local scriptEventDefinition = ScriptEvent("Script_Event", typeid("")) -- Event address is of string type
local method0 = scriptEventDefinition:AddMethod("MethodWithId0", typeid(false)) -- Return value is Boolean
method0:AddParameter("Param0", typeid(0))
method0:AddParameter("Param1", typeid(EntityId()))
scriptEventDefinition:AddMethod("MethodWithId1") -- No return, no parameters
scriptEventDefinition:Register()
ScriptTrace("Need to connect to bus!")
scriptEventHandler = Script_Event.Connect(luaScriptEventWithId, "ScriptEventAddress")
ScriptTrace("Should be connected !")
local returnValue = Script_Event.Event.MethodWithId0("ScriptEventAddress", 1, EntityId(12345))
ScriptExpectTrue(returnValue, "Method0's return value must be true [ScriptEventRefactor_LuaScriptEventWithId]")
Script_Event.Event.MethodWithId1("ScriptEventAddress")
)";
AZ::ScriptContext script;
script.BindTo(m_behaviorContext);
script.Execute(luaCode);
script.GarbageCollect();
}
//////////////////////////////////////////////////////////////////////////]
TEST_F(ScriptEventsTestFixture, ScriptEventRefactor_LuaScriptEventBroadcast)
{
// Tests Script Events that rely on broadcast buses (No address).
const char luaCode[] =
R"( luaScriptEventBroadcast = {
BroadcastMethod0 = function(self, param1, param2)
ScriptTrace("Handler: " .. tostring(param1) .. " " .. tostring(param2))
ScriptExpectTrue(typeid(param1) == typeid(0), "Type of param1 must be "..tostring(typeid(0)))
ScriptExpectTrue(typeid(param2) == typeid(EntityId()), "Type of param2 must be "..tostring(typeid(EntityId())))
ScriptExpectTrue(param1 == 2, "The first parameter must be 2")
ScriptExpectTrue(param2 == EntityId(23456), "The received entity Id must match the one sent")
ScriptTrace("BroadcastMethod0 Called")
return true
end,
BroadcastMethod1 = function(self)
ScriptTrace("BroadcastMethod1 Called")
end
}
local scriptEventDefinition = ScriptEvent("Script_Broadcast")
local method0 = scriptEventDefinition:AddMethod("BroadcastMethod0", typeid(false))
method0:AddParameter("Param0", typeid(0))
method0:AddParameter("Param1", typeid(EntityId()))
scriptEventDefinition:AddMethod("BroadcastMethod1")
scriptEventDefinition:Register()
scriptEventHandler = Script_Broadcast.Connect(luaScriptEventBroadcast)
local returnValue = Script_Broadcast.Broadcast.BroadcastMethod0(2, EntityId(23456))
ScriptExpectTrue(returnValue, "BroadcastMethod0's return value must be true [ScriptEventRefactor_LuaScriptEventBroadcast]")
-- Broadcast an event without return or parameters
Script_Broadcast.Broadcast.BroadcastMethod1()
)";
AZ::ScriptContext script;
script.BindTo(m_behaviorContext);
script.Execute(luaCode);
script.GarbageCollect();
}
//////////////////////////////////////////////////////////////////////////]
TEST_F(ScriptEventsTestFixture, ScriptEventRefactor_LuaVersionedProperties)
{
// Tests the VersionedProperty's Lua API
const char luaCode[] =
R"(
local versionProperty0 = VersionedProperty("Hello")
versionProperty0:Set("World")
ScriptExpectTrue(versionProperty0:Get() == "World", "Version property should match the latest version (i.e. World).")
local versionedNumberProperty = VersionedProperty(1234)
versionedNumberProperty:Set(4321)
versionedNumberProperty:Set(5555)
ScriptExpectTrue(versionedNumberProperty:Get() == 5555, "Number must match latest version")
local versionedEntityIDProperty = VersionedProperty(EntityId())
versionedEntityIDProperty:Set(EntityId(123))
versionedEntityIDProperty:Set(EntityId(321))
ScriptExpectTrue(versionedEntityIDProperty:Get() == EntityId(321), "EntityId must match latest version")
)";
AZ::ScriptContext script;
script.BindTo(m_behaviorContext);
script.Execute(luaCode);
script.GarbageCollect();
}
//////////////////////////////////////////////////////////////////////////]
class ScriptEventHandlerHook
{
public:
static void OnEventGenericHook(void* userData, const char* eventName, int eventIndex, AZ::BehaviorValueParameter* result, int numParameters, AZ::BehaviorValueParameter* parameters)
{
ScriptEventHandlerHook* handler(reinterpret_cast<ScriptEventHandlerHook*>(userData));
handler->OnEvent(eventName, eventIndex, result, numParameters, parameters);
}
// This is the actual handler that will be called if the event is sent or broadcast
void OnEvent(const char* eventName, const int eventIndex, [[maybe_unused]] AZ::BehaviorValueParameter* result, const int numParameters, AZ::BehaviorValueParameter* parameters)
{
EXPECT_EQ(eventIndex, 0);
EXPECT_EQ(numParameters, 1);
for (int parameterIndex(0); parameterIndex < numParameters; ++parameterIndex)
{
const auto& value = *(parameters + parameterIndex);
EXPECT_EQ(value.m_typeId, azrtti_typeid<AZ::EntityId>());
}
EXPECT_TRUE(true) << "Received Event: " << eventName;
}
};
class AssetEventHandler
: public AZ::Data::AssetBus::Handler
{
public:
using Callback = AZStd::function<void()>;
AssetEventHandler(AZ::Data::AssetId assetId, Callback onReady= []() {}, Callback onSaved = []() {})
: m_assetId(assetId)
, m_onReadyCallback(onReady)
, m_onSavedCallback(onSaved)
, m_ready(0)
, m_saved(0)
, m_unloaded(0)
{
}
~AssetEventHandler()
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
bool IsDone()
{
AZ::Data::AssetBus::ExecuteQueuedEvents();
return !AZ::Data::AssetBus::Handler::BusIsConnected() || m_ready == 1 || m_saved == 1;
}
void OnAssetMoved(AZ::Data::Asset<AZ::Data::AssetData>, void*) override
{
EXPECT_TRUE(false);
}
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData>) override
{
EXPECT_TRUE(false);
}
void OnAssetUnloaded(const AZ::Data::AssetId, const AZ::Data::AssetType) override
{
m_unloaded++;
AZ::Data::AssetBus::Handler::BusDisconnect();
}
void OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> assetData) override
{
EXPECT_TRUE(false);
AZ::Data::AssetBus::Handler::BusDisconnect();
}
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData>) override
{
m_ready++;
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetManager::Instance().DispatchEvents();
m_onReadyCallback();
}
void OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, [[maybe_unused]] bool isSuccessful) override
{
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetManager::Instance().DispatchEvents();
m_saved++;
m_onSavedCallback();
}
AZStd::atomic_int m_saved;
AZStd::atomic_int m_ready;
AZStd::atomic_int m_unloaded;
AZ::Data::AssetId m_assetId;
Callback m_onReadyCallback;
Callback m_onSavedCallback;
};
void WaitForAssetSystem(AZStd::function<bool()> condition)
{
while (!condition())
{
AZ::Data::AssetBus::ExecuteQueuedEvents();
AZ::Data::AssetManager::Instance().DispatchEvents();
AZStd::this_thread::yield();
}
}
TEST_F(ScriptEventsTestFixture, ScriptEventRefactor_BehaviorContextBinding)
{
// Tests the C++ API for Script Event definition, this is meant for testing the core code only
// Script Events are designed as a data side feature and should not be created using C++
using namespace AZ;
using namespace ScriptEvents;
using namespace ScriptEventData;
const AZStd::string scriptEventName = "SCRIPTEVENT";
ScriptEvent definition;
definition.GetNameProperty().Set(scriptEventName);
definition.GetAddressTypeProperty().Set(azrtti_typeid<AZ::EntityId>());
Method& method0 = definition.NewMethod();
method0.GetNameProperty().Set("Method");
// Necessary because when working in the Editor, a change to the property will trigger a backup of the property prior to
// creating the new version, it's not really intuitive in the context of this test and API, but it's meant as an editor
// side feature more so than a code feature
method0.GetNameProperty().OnPropertyChange();
auto& method1name = method0.GetNameProperty().NewVersion();
method1name.Set("NewMethodName");
Parameter& parameter0 = method0.NewParameter();
parameter0.GetNameProperty().Set("Parameter");
parameter0.GetTooltipProperty().Set("A simple numeric parameter");
parameter0.GetTypeProperty().Set(azrtti_typeid<AZ::EntityId>());
parameter0.GetNameProperty().OnPropertyChange(); // See comment above
auto& parameterNameV1 = parameter0.GetNameProperty().NewVersion();
parameterNameV1.Set("RenamedParameter");
AZ::Uuid assetId = AZ::Uuid("{5B933982-7741-47B4-9060-945A6DFF1D75}");
// Create an asset out of our Script Event
const AZ::Data::AssetType type = AZ::AzTypeInfo<ScriptEvents::ScriptEventsAsset>::Uuid();
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> assetData = AZ::Data::AssetManager::Instance().CreateAsset(assetId, type, AZ::Data::AssetLoadBehavior::Default);
ScriptEvents::ScriptEventsAsset* scriptAsset = assetData.Get();
scriptAsset->m_definition = definition;
EXPECT_TRUE(assetData.Save());
AssetEventHandler assetHandler(assetId);
assetHandler.BusConnect(assetId);
WaitForAssetSystem([&]() { return assetHandler.IsDone(); });
assetHandler.BusDisconnect();
ScriptEvents::Internal::ScriptEvent scriptEventV0;
assetData = AZ::Data::AssetManager::Instance().FindOrCreateAsset<ScriptEvents::ScriptEventsAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
scriptEventV0.CompleteRegistration(assetData);
WaitForAssetSystem([&]() { return scriptEventV0.IsReady(); });
// Install the handler
AZ::BehaviorEBusHandler* handler = nullptr;
AZ::BehaviorEBus* behaviorEbus = scriptEventV0.GetBehaviorBus();
EXPECT_TRUE(behaviorEbus->m_createHandler->InvokeResult(handler));
ScriptEventHandlerHook scriptEventHandler;
EXPECT_TRUE(handler->InstallGenericHook(method0.GetName().data(), &ScriptEventHandlerHook::OnEventGenericHook, &scriptEventHandler));
// Randomly chosen address using EntityId as address type
AZ::BehaviorValueParameter addressParameter;
AZ::EntityId address = AZ::EntityId(0x12345);
addressParameter.Set(&address);
// Connect the handler to an address
EXPECT_TRUE(handler->Connect(&addressParameter));
// Now, having defined a ScriptEvent and installed a handler, test sending an Event and Broadcasting
AZ::BehaviorMethod* behaviorMethod0 = nullptr;
if (scriptEventV0.GetMethod(method0.GetName(), behaviorMethod0))
{
const AZ::BehaviorParameter* address2 = behaviorMethod0->GetArgument(0);
EXPECT_EQ(address2->m_typeId, azrtti_typeid<AZ::EntityId>());
const AZ::BehaviorParameter* argument = behaviorMethod0->GetArgument(1);
if (argument)
{
EXPECT_EQ(argument->m_typeId, azrtti_typeid<AZ::EntityId>());
}
AZStd::array<AZ::BehaviorValueParameter, 2> params;
AZ::BehaviorValueParameter* paramFirst(params.begin());
AZ::BehaviorValueParameter* paramIter = paramFirst;
// Set the values
AZ::EntityId value = AZ::EntityId(0x12345);
paramIter->Set(&value); // Bus address
++paramIter;
AZ::EntityId value2 = AZ::EntityId(0x20000);
paramIter->Set(&value2); // Some payload (first parameter) can be any entity
for (size_t argIndex(0), sentinel(behaviorMethod0->GetNumArguments() - 1); argIndex != sentinel; ++argIndex)
{
if (const AZ::BehaviorParameter* argument2 = behaviorMethod0->GetArgument(argIndex))
{
const AZStd::string argumentTypeName = argument2->m_typeId.ToString<AZStd::string>();
const AZStd::string* argumentNamePtr = behaviorMethod0->GetArgumentName(argIndex);
const AZStd::string argName = argumentNamePtr && !argumentNamePtr->empty()
? *argumentNamePtr
: (AZStd::string::format("%s:%zu", argumentTypeName.c_str(), argIndex));
AZ_TracePrintf("Script Events", "(%d): %s : %s\n", argIndex, argName.c_str(), argumentTypeName.c_str());
}
}
// This is the behavior of sending a Script Event, will be handled by any connected handlers
EXPECT_TRUE(behaviorMethod0->Call(paramFirst, aznumeric_cast<unsigned int>(params.size())));
}
else
{
ADD_FAILURE() << "The Script Event for " << scriptEventName.c_str() << " does not exist";
}
handler->Disconnect();
scriptEventV0.BusDisconnect();
EXPECT_TRUE(behaviorEbus->m_destroyHandler->Invoke(handler));
auto onReady = [&assetData, &scriptEventName]() {
const char* renamedMethod = "__METHOD__1__";
ScriptEvents::ScriptEventsAsset* loadedScriptAsset = assetData.GetAs<ScriptEvents::ScriptEventsAsset>();
EXPECT_TRUE(loadedScriptAsset);
const ScriptEvents::ScriptEvent& loadedDefinition = loadedScriptAsset->m_definition;
EXPECT_EQ(loadedDefinition.GetVersion(), 0);
EXPECT_STREQ(loadedDefinition.GetName().data(), scriptEventName.c_str());
ScriptEvents::Method method;
bool foundMethod = loadedDefinition.FindMethod(renamedMethod, method);
EXPECT_TRUE(foundMethod);
EXPECT_EQ(method.GetNameProperty().GetVersion(), 1);
assetData = {};
};
AssetEventHandler assetHandler2(assetId, []() {}, []() {});
assetHandler2.BusConnect(assetId);
scriptAsset = {};
assetData = {};
WaitForAssetSystem([&]() { return assetHandler2.m_unloaded == 1; });
}
//////////////////////////////////////////////////////////////////////////]
TEST_F(ScriptEventsTestFixture, ScriptEventRefactor_SerializationAndVersioning)
{
// Tests serialization to file of the Script Event definition format and
// associated data types (i.e. VersionedProperty)
using namespace AZ;
using namespace ScriptEvents;
using namespace ScriptEventData;
ScriptEvent definition;
AZStd::string scriptEventName = "__SCRIPT_EVENT_NAME__";
definition.SetVersion(0);
definition.GetNameProperty().Set(scriptEventName);
definition.GetTooltipProperty().Set("This is an example script event.");
Method& method0 = definition.NewMethod();
method0.GetNameProperty().Set("__METHOD__0__");
method0.GetTooltipProperty().Set("This is an example method");
Parameter& parameter0 = method0.NewParameter();
parameter0.GetNameProperty().Set("__PARAMETER__0__");
parameter0.GetTooltipProperty().Set("A simple numeric parameter");
parameter0.GetTypeProperty().Set(azrtti_typeid<int>());
const char* renamedParameter = "__RENAMED_PARAMETER__0__";
// Necessary because when working in the Editor, a change to the property will trigger a backup of the property prior to
// creating the new version, it's not really intuitive in the context of this test and API, but it's meant as an editor
// side feature moreso than a code feature
method0.GetNameProperty().OnPropertyChange();
VersionedProperty& parameterName = parameter0.GetNameProperty().NewVersion();
parameterName.Set(renamedParameter);
const char* renamedMethod = "__METHOD__1__";
method0.GetNameProperty().OnPropertyChange(); // See comment above
VersionedProperty& methodName = method0.GetNameProperty().NewVersion();
methodName.Set(renamedMethod);
const AZStd::string& latestMethodName = method0.GetName();
EXPECT_STREQ(renamedMethod, latestMethodName.c_str());
const AZStd::string& latestParameterdName = parameter0.GetName();
EXPECT_STREQ(renamedParameter, latestParameterdName.c_str());
//////////////////////////////////////////////////////////////////////////
/// Serialize the data
AZStd::vector<char> xmlBuffer;
IO::ByteContainerStream<AZStd::vector<char> > xmlStream(&xmlBuffer);
ObjectStream* xmlObjStream = ObjectStream::Create(&xmlStream, *m_serializeContext, ObjectStream::ST_XML);
xmlObjStream->WriteClass(&definition);
xmlObjStream->Finalize();
AZ::IO::SystemFile tmpOut;
tmpOut.Open("ScriptEvents_SerializationTest_Full.xml", AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
tmpOut.Write(xmlStream.GetData()->data(), xmlStream.GetLength());
tmpOut.Close();
FlattenVersionedPropertiesInObject(m_serializeContext, &definition);
xmlBuffer.clear();
xmlStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
xmlObjStream = ObjectStream::Create(&xmlStream, *m_serializeContext, ObjectStream::ST_XML);
xmlObjStream->WriteClass(&definition);
xmlObjStream->Finalize();
tmpOut.Open("ScriptEvents_SerializationTest_Flat.xml", AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY);
tmpOut.Write(xmlStream.GetData()->data(), xmlStream.GetLength());
tmpOut.Close();
// Create the asset
const AZ::Uuid& assetId = AZ::Uuid("{0B4F3716-59D4-4BA6-8982-9C7CCB91C113}");
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> assetData = AZ::Data::AssetManager::Instance().CreateAsset<ScriptEvents::ScriptEventsAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
ScriptEvents::ScriptEventsAsset* scriptAsset = assetData.Get();
scriptAsset->m_definition = definition;
EXPECT_TRUE(assetData.Save());
AssetEventHandler assetHandler(assetId);
assetHandler.BusConnect(assetId);
WaitForAssetSystem([&]() { return assetHandler.IsDone(); });
bool result = false;
AZ::IO::FileIOStream outFileStream("ScriptEvents_TestAsset.xml", AZ::IO::OpenMode::ModeWrite);
if (outFileStream.IsOpen())
{
EXPECT_TRUE(AZ::Utils::SaveObjectToStream<ScriptEvents::ScriptEventsAsset>(outFileStream,
AZ::ObjectStream::ST_XML,
assetData.Get(),
m_serializeContext));
}
AssetEventHandler assetHandler2(assetId, []() {}, []() {});
assetHandler2.BusConnect(assetId);
assetData = {};
scriptAsset = {};
WaitForAssetSystem([&]() { return assetHandler2.m_unloaded == 1; });
auto onSaved = [&assetId]()
{
AZStd::string scriptEventName = "__SCRIPT_EVENT_NAME__";
const char* renamedMethod = "__METHOD__1__";
// Load the asset
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> assetData = AZ::Data::AssetManager::Instance().GetAsset<ScriptEvents::ScriptEventsAsset>(assetId, AZ::Data::AssetLoadBehavior::Default);
ScriptEvents::ScriptEventsAsset* loadedScriptAsset = assetData.GetAs<ScriptEvents::ScriptEventsAsset>();
EXPECT_TRUE(loadedScriptAsset);
const ScriptEvents::ScriptEvent& loadedDefinition = loadedScriptAsset->m_definition;
EXPECT_EQ(loadedDefinition.GetVersion(), 0);
EXPECT_STREQ(loadedDefinition.GetName().data(), scriptEventName.c_str());
ScriptEvents::Method method;
bool foundMethod = loadedDefinition.FindMethod(renamedMethod, method);
EXPECT_TRUE(foundMethod);
EXPECT_EQ(method.GetNameProperty().GetVersion(), 1);
AssetEventHandler assetHandler(assetData.GetId());
assetHandler.BusConnect(assetId);
assetData = {};
WaitForAssetSystem([&]() { return assetHandler.m_unloaded == 1; });
assetHandler.BusDisconnect();
};
AssetEventHandler assetHandler3(assetData.GetId(), []() {}, onSaved);
WaitForAssetSystem([&]() { return assetHandler3.IsDone(); });
assetHandler3.BusDisconnect();
auto verifyAsset = AZ::Data::AssetManager::Instance().FindAsset(assetId, AZ::Data::AssetLoadBehavior::Default);
EXPECT_FALSE(verifyAsset);
}
}
@@ -0,0 +1,44 @@
#
# 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.
#
set(FILES
Source/ScriptEventsSystemComponent.h
Source/ScriptEventsSystemComponent.cpp
Include/ScriptEvents/ScriptEventsGem.h
Include/ScriptEvents/ScriptEventsAsset.h
Include/ScriptEvents/ScriptEventsAssetRef.h
Include/ScriptEvents/ScriptEventsBus.h
Include/ScriptEvents/ScriptEventFundamentalTypes.h
Include/ScriptEvents/ScriptEventDefinition.h
Include/ScriptEvents/ScriptEventDefinition.cpp
Include/ScriptEvents/ScriptEvent.h
Include/ScriptEvents/ScriptEventMethod.h
Include/ScriptEvents/ScriptEvent.cpp
Include/ScriptEvents/ScriptEventParameter.h
Include/ScriptEvents/ScriptEventSystem.h
Include/ScriptEvents/ScriptEventSystem.cpp
Include/ScriptEvents/ScriptEventTypes.h
Include/ScriptEvents/ScriptEventTypes.cpp
Include/ScriptEvents/Internal/VersionedProperty.h
Include/ScriptEvents/Internal/VersionedProperty.cpp
Include/ScriptEvents/Internal/BehaviorContextBinding/BehaviorContextFactoryMethods.h
Include/ScriptEvents/Internal/BehaviorContextBinding/DefaultEventHandler.h
Include/ScriptEvents/Internal/BehaviorContextBinding/DefaultEventHandler.cpp
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.h
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventMethod.cpp
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.h
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBroadcast.cpp
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventsBindingBus.h
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.h
Include/ScriptEvents/Internal/BehaviorContextBinding/ScriptEventBinding.cpp
Include/ScriptEvents/Components/ScriptEventReferencesComponent.h
Include/ScriptEvents/Components/ScriptEventReferencesComponent.cpp
)
@@ -0,0 +1,18 @@
#
# 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.
#
set(FILES
Builder/ScriptEventsBuilderComponent.cpp
Builder/ScriptEventsBuilderComponent.h
Builder/ScriptEventsBuilderWorker.cpp
Builder/ScriptEventsBuilderWorker.h
Builder/BuilderSystemComponent.h
)
@@ -0,0 +1,18 @@
#
# 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.
#
set(FILES
Source/precompiled.cpp
Source/precompiled.h
Source/Editor/ScriptEventsEditorGem.cpp
Source/Editor/ScriptEventsSystemEditorComponent.cpp
Source/Editor/ScriptEventsSystemEditorComponent.h
)
@@ -0,0 +1,16 @@
#
# 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.
#
set(FILES
Source/precompiled.cpp
Source/precompiled.h
Source/ScriptEventsGem.cpp
)
@@ -0,0 +1,20 @@
#
# 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.
#
set(FILES
Tests/ScriptEventsTest.cpp
Tests/ScriptEventsTestApplication.h
Tests/ScriptEventsTestFixture.h
Tests/ScriptEventsTestFixture.cpp
Tests/ScriptEventTestUtilities.cpp
Tests/ScriptEventTestUtilities.h
Tests/Tests/ScriptEventsTest_Core.cpp
)
+20
View File
@@ -0,0 +1,20 @@
{
"GemFormatVersion": 4,
"Uuid": "32d8ba21703e4bbbb08487366e48dd69",
"Name": "ScriptEvents",
"DisplayName": "Script Events",
"Version": "0.1.0",
"Summary": "Provides a framework for creating event assets usable from any scripting solution.",
"Tags": [ "Script", "Events", "Lua", "Script", "Canvas", "EBus", "Behavior Context" ],
"IconPath": "preview.png",
"Modules": [
{
"Type": "GameModule"
},
{
"Name": "Editor",
"Type": "EditorModule",
"Extends": "GameModule"
}
]
}
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f36fe261d042dd8c937fad8ea82d3cb88e92645288f458c012f225d0ea6ebbf5
size 1877