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,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;
};
}