You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/ScriptEvents/Code/Include/ScriptEvents/ScriptEventDefinition.cpp

169 lines
7.2 KiB
C++

/*
* 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);
}
}