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.
714 lines
33 KiB
C++
714 lines
33 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include "ExecutionInterpretedAPI.h"
|
|
|
|
#include <AzCore/Math/Crc.h>
|
|
#include <AzCore/Script/ScriptContext.h>
|
|
#include <AzCore/Script/ScriptSystemBus.h>
|
|
#include <AzCore/Script/lua/lua.h>
|
|
#include <ScriptCanvas/Asset/RuntimeAsset.h>
|
|
#include <ScriptCanvas/Core/Nodeable.h>
|
|
#include <ScriptCanvas/Core/NodeableOut.h>
|
|
#include <ScriptCanvas/Execution/Interpreted/ExecutionStateInterpreted.h>
|
|
#include <ScriptCanvas/Execution/Interpreted/ExecutionStateInterpretedUtility.h>
|
|
#include <ScriptCanvas/Execution/NodeableOut/NodeableOutNative.h>
|
|
#include <ScriptCanvas/Grammar/PrimitivesDeclarations.h>
|
|
#include <ScriptCanvas/Libraries/Math/MathNodeUtilities.h>
|
|
#include <ScriptCanvas/Utils/BehaviorContextUtils.h>
|
|
|
|
#include "ExecutionInterpretedCloningAPI.h"
|
|
#include "ExecutionInterpretedDebugAPI.h"
|
|
#include "ExecutionInterpretedEBusAPI.h"
|
|
#include "ExecutionInterpretedOut.h"
|
|
|
|
namespace ExecutionInterpretedAPICpp
|
|
{
|
|
using namespace ScriptCanvas;
|
|
using namespace ScriptCanvas::Execution;
|
|
|
|
constexpr size_t k_StringFastSize = 32;
|
|
|
|
constexpr size_t k_UuidSize = 16;
|
|
|
|
constexpr unsigned char k_Bad = 77;
|
|
|
|
constexpr const char* const k_fastDigits = "0123456789ABCDEF";
|
|
|
|
constexpr unsigned char k_fastValues[] =
|
|
{
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, k_Bad,k_Bad,k_Bad,k_Bad,k_Bad,k_Bad,k_Bad, 10, 11, 12, 13, 14, 15
|
|
};
|
|
|
|
[[maybe_unused]] constexpr unsigned char k_FastValuesIndexSentinel = 'G' - '0';
|
|
|
|
template<typename T>
|
|
T* GetAs(AZ::BehaviorValueParameter& argument)
|
|
{
|
|
return argument.m_typeId == azrtti_typeid<T>()
|
|
? reinterpret_cast<T*>(argument.GetValueAddress())
|
|
: nullptr;
|
|
}
|
|
|
|
int DeleteNodeable(lua_State* lua)
|
|
{
|
|
AZ::LuaUserData* userData = reinterpret_cast<AZ::LuaUserData*>(lua_touserdata(lua, -1));
|
|
AZ_Assert(userData && userData->magicData == AZ_CRC_CE("AZLuaUserData"), "this isn't user data");
|
|
delete reinterpret_cast<ScriptCanvas::Nodeable*>(userData->value);
|
|
userData->value = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
int ErrorHandler(lua_State* lua)
|
|
{
|
|
if (lua_isstring(lua, -1))
|
|
{
|
|
AZ::ScriptContext::FromNativeContext(lua)->Error(AZ::ScriptContext::ErrorType::Error, true, "%s", lua_tostring(lua, -1));
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("ScriptCanvas", false, "First argument to ScriptCanvas interpreted ErrorHandler must be a string, not %s.", luaL_typename(lua, -1));
|
|
}
|
|
|
|
lua_pop(lua, 1);
|
|
return 0;
|
|
}
|
|
|
|
int ProxyNewIndexMethod(lua_State* lua)
|
|
{
|
|
// Lua: ud, k, v
|
|
lua_pushvalue(lua, lua_upvalueindex(1));
|
|
// Lua: ud, k, v, ?
|
|
AZ_Assert(lua_istable(lua, -1), "the first upvalue must be a table");
|
|
// Lua: ud, k, v, proxy
|
|
lua_replace(lua, -4);
|
|
// Lua: proxy k, v
|
|
lua_rawset(lua, -3);
|
|
// Lua: proxy
|
|
lua_pop(lua, 1);
|
|
// Lua:
|
|
return 0;
|
|
}
|
|
|
|
int TypeSafeEBusResultBoolean(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
lua_pushboolean(lua, false);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int TypeSafeEBusResultFromEntityId(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
AZ::ScriptValue<Data::EntityIDType>::StackPush(lua, Data::EntityIDType());
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int TypeSafeEBusResultFromNamedEntityId(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
AZ::ScriptValue<Data::NamedEntityIDType>::StackPush(lua, Data::NamedEntityIDType());
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int TypeSafeEBusResultNumber(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
lua_pushnumber(lua, lua_Number(0.0f));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int TypeSafeEBusResultString(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
lua_pushstring(lua, "");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
template<typename t_Type>
|
|
int TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType(lua_State* lua)
|
|
{
|
|
if (lua_isnil(lua, -1))
|
|
{
|
|
lua_pop(lua, 1);
|
|
AZ::ScriptValue<t_Type>::StackPush(lua, Data::Traits<t_Type>::GetDefault());
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int TypeSafeEBusMultipleReturnResults(lua_State* lua)
|
|
{
|
|
// Lua: ?, typeId
|
|
if (lua_isnil(lua, -2))
|
|
{
|
|
AZ_Assert(lua_isstring(lua, -1), "error in compiled lua file. TypeSafeEBusMultipleReturnResults expected string 2nd argument, got %s", lua_typename(lua, -1));
|
|
// Lua: nil, aztypeidStr
|
|
const char* aztypeidStr = lua_tostring(lua, -1);
|
|
AZ::TypeId typeId = CreateIdFromStringFast(aztypeidStr);
|
|
lua_pop(lua, 2);
|
|
// Lua:
|
|
AZStd::pair<void*, AZ::BehaviorContext*> multipleResults = ScriptCanvas::BehaviorContextUtils::ConstructTupleGetContext(typeId);
|
|
AZ_Assert(multipleResults.first, "failure to construct a tuple by typeid from behavior context");
|
|
AZ::BehaviorValueParameter parameter;
|
|
parameter.m_value = multipleResults.first;
|
|
parameter.m_typeId = typeId;
|
|
AZ::StackPush(lua, multipleResults.second, parameter);
|
|
}
|
|
else
|
|
{
|
|
// Lua: AZStd::tuple, typeId
|
|
lua_pop(lua, 1);
|
|
}
|
|
// Lua: AZStd::tuple
|
|
return 1;
|
|
}
|
|
|
|
void RegisterTypeSafeEBusResultFunctions(lua_State* lua)
|
|
{
|
|
// all the single return types that are value types in ScriptCanvas, but could be returned as nil by event-results calls from Lua...
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::AABB()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::AABBType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::AssetId()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::AssetIdType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Boolean()).data(), &TypeSafeEBusResultBoolean);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Color()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::ColorType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::CRC()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::CRCType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::EntityID()).data(), &TypeSafeEBusResultFromEntityId);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Matrix3x3()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::Matrix3x3Type>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Matrix4x4()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::Matrix3x3Type>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::NamedEntityID()).data(), &TypeSafeEBusResultFromNamedEntityId);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Number()).data(), &TypeSafeEBusResultNumber);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::OBB()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::OBBType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Plane()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::PlaneType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Quaternion()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::QuaternionType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::String()).data(), &TypeSafeEBusResultString);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Transform()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::TransformType>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Vector2()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::Vector2Type>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Vector3()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::Vector3Type>);
|
|
lua_register(lua, Grammar::ToTypeSafeEBusResultName(Data::Type::Vector4()).data(), &TypeSafeEBusResultFromBehaviorContextObjectThatIsValueType<Data::Vector4Type>);
|
|
// multiple results in the form of tuple are all handled the same way
|
|
lua_register(lua, Grammar::k_TypeSafeEBusMultipleResultsName, &TypeSafeEBusMultipleReturnResults);
|
|
}
|
|
}
|
|
|
|
namespace ScriptCanvas
|
|
{
|
|
namespace Execution
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// \note make sure temporary allocations with methods that have a result are handled properly with memory model
|
|
// \see LuaScriptCaller(BehaviorContext* context, BehaviorMethod* method)
|
|
// \see m_resultToLua = ToLuaStack(context, method->GetResult(), &m_prepareResult, m_resultClass);
|
|
// \note make sure the ebus case doesn't allocate extra space as it will always have space for the result
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void ActivateInterpreted()
|
|
{
|
|
AZ::ScriptContext* scriptContext{};
|
|
AZ::ScriptSystemRequestBus::BroadcastResult(scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
|
|
AZ_Assert(scriptContext, "Must have a default script context");
|
|
RegisterAPI(scriptContext->NativeContext());
|
|
}
|
|
|
|
void SetInterpretedExecutionMode(BuildConfiguration configuration)
|
|
{
|
|
switch (configuration)
|
|
{
|
|
case ScriptCanvas::BuildConfiguration::Debug:
|
|
SetInterpretedExecutionModeDebug();
|
|
break;
|
|
case ScriptCanvas::BuildConfiguration::Performance:
|
|
SetInterpretedExecutionModePerformance();
|
|
break;
|
|
case ScriptCanvas::BuildConfiguration::Release:
|
|
SetInterpretedExecutionModeRelease();
|
|
break;
|
|
default:
|
|
AZ_Assert(false, "unhandled BuildConfiguration type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetInterpretedExecutionModeDebug()
|
|
{
|
|
AZ::ScriptContext* scriptContext{};
|
|
AZ::ScriptSystemRequestBus::BroadcastResult(scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
|
|
AZ_Assert(scriptContext, "Must have a default script context");
|
|
lua_State* lua = scriptContext->NativeContext();
|
|
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationRelease);
|
|
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationPerformance);
|
|
}
|
|
|
|
void SetInterpretedExecutionModePerformance()
|
|
{
|
|
AZ::ScriptContext* scriptContext{};
|
|
AZ::ScriptSystemRequestBus::BroadcastResult(scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
|
|
AZ_Assert(scriptContext, "Must have a default script context");
|
|
lua_State* lua = scriptContext->NativeContext();
|
|
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationRelease);
|
|
|
|
lua_pushboolean(lua, true);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationPerformance);
|
|
}
|
|
|
|
void SetInterpretedExecutionModeRelease()
|
|
{
|
|
AZ::ScriptContext* scriptContext{};
|
|
AZ::ScriptSystemRequestBus::BroadcastResult(scriptContext, &AZ::ScriptSystemRequests::GetContext, AZ::ScriptContextIds::DefaultScriptContextId);
|
|
AZ_Assert(scriptContext, "Must have a default script context");
|
|
lua_State* lua = scriptContext->NativeContext();
|
|
|
|
lua_pushboolean(lua, true);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationRelease);
|
|
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, Grammar::k_InterpretedConfigurationPerformance);
|
|
}
|
|
|
|
AZ::IRttiHelper* GetRttiHelper(const AZ::Uuid& azTypeId, AZ::BehaviorContext& behaviorContext)
|
|
{
|
|
auto bcClassIter = behaviorContext.m_typeToClassMap.find(azTypeId);
|
|
return bcClassIter != behaviorContext.m_typeToClassMap.end() ? bcClassIter->second->m_azRtti : nullptr;
|
|
}
|
|
|
|
AZ::BehaviorValueParameter BehaviorValueParameterFromTypeIdString(const char* aztypeidStr, AZ::BehaviorContext& behaviorContext)
|
|
{
|
|
AZ::BehaviorValueParameter bvp;
|
|
bvp.m_typeId = CreateIdFromStringFast(aztypeidStr);
|
|
bvp.m_azRtti = GetRttiHelper(bvp.m_typeId, behaviorContext);
|
|
return bvp;
|
|
}
|
|
|
|
AZStd::string CreateStringFastFromId(const AZ::Uuid& uuid)
|
|
{
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
static_assert(sizeof(AZ::Uuid) == k_UuidSize, "size of uuid has changed");
|
|
|
|
AZStd::string fastString;
|
|
fastString.resize(k_StringFastSize);
|
|
char* fastDigit = fastString.begin();
|
|
|
|
for (size_t i = 0; i < k_UuidSize; ++i)
|
|
{
|
|
const unsigned char value = uuid.data[i];
|
|
*fastDigit = k_fastDigits[value >> 4];
|
|
++fastDigit;
|
|
*fastDigit = k_fastDigits[value & 15];
|
|
++fastDigit;
|
|
}
|
|
|
|
return fastString;
|
|
}
|
|
|
|
AZ::Uuid CreateIdFromStringFast(const char* string)
|
|
{
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
|
|
AZ_Assert(string, "string argument must not be null");
|
|
AZ_Assert(strlen(string) == k_StringFastSize, "invalid length of string, fast format must be: 0123456789ABCDEF0123456789ABCDEF");
|
|
const char* current = string;
|
|
|
|
AZ::Uuid id;
|
|
auto data = id.begin();
|
|
auto sentinel = id.end();
|
|
|
|
do
|
|
{
|
|
const auto index0 = *current - '0';
|
|
AZ_Assert(index0 < k_FastValuesIndexSentinel, "invalid value index in found in fast string, fast format must be: 0123456789ABCDEF0123456789ABCDEF");
|
|
const auto value0 = k_fastValues[index0];
|
|
AZ_Assert(value0 != k_Bad, "invalid value in fast string, fast format must be: 0123456789ABCDEF0123456789ABCDEF");
|
|
++current;
|
|
|
|
const auto index1 = *current - '0';
|
|
AZ_Assert(index1 < k_FastValuesIndexSentinel, "invalid value index in found in fast string, fast format must be: 0123456789ABCDEF0123456789ABCDEF");
|
|
const auto value1 = k_fastValues[index1];
|
|
AZ_Assert(value1 != k_Bad, "invalid value in fast string, fast format must be: 0123456789ABCDEF0123456789ABCDEF");
|
|
++current;
|
|
|
|
*data = (value0 << 4) | value1;
|
|
} while ((++data) != sentinel);
|
|
|
|
return id;
|
|
}
|
|
|
|
int OverrideNodeableMetatable(lua_State* lua)
|
|
{
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
|
|
/**
|
|
Here is the function in Lua for easier reading
|
|
function OverrideNodeableMetatable(userdata, class_mt)
|
|
local proxy = setmetatable({}, class_mt) -- class_mt from the user graph definition
|
|
local instance_mt = {
|
|
__index = proxy
|
|
__newindex = function(t, k, v)
|
|
proxy[k] = v
|
|
end,
|
|
}
|
|
--[[ -- not being done
|
|
for name, method in pairs(metamethods) do
|
|
if name ~= '__index' and name ~= '__newindex' do
|
|
instance_mt[name] = method
|
|
end
|
|
end
|
|
--]]
|
|
return setmetatable(userdata instance_mt) -- can't be done from Lua
|
|
end
|
|
--[[
|
|
userdata to Nodeable before:
|
|
getmetatable(userdata).__index == Nodeable
|
|
|
|
userdata to Nodeable after:
|
|
local override_mt = getmetatable(userdata)
|
|
local proxy = override_mt.__index
|
|
local SubGraph = getmetatable(proxy).__index
|
|
getmetatable(SubGraph.__index) == Nodeable)
|
|
--]]
|
|
*/
|
|
|
|
// \note: all other metamethods ignored for now
|
|
// \note: the the object is being constructed, and is assumed to never leave or re-enter Lua again
|
|
AZ_Assert(lua_isuserdata(lua, -2) && !lua_islightuserdata(lua, -2), "Error in compiled lua file, 1st argument to OverrideNodeableMetatable is not userdata (Nodeable)");
|
|
AZ_Assert(lua_istable(lua, -1), "Error in compiled lua file, 2nd argument to OverrideNodeableMetatable is not a Lua table");
|
|
|
|
[[maybe_unused]] auto userData = reinterpret_cast<AZ::LuaUserData*>(lua_touserdata(lua, -2));
|
|
AZ_Assert(userData && userData->magicData == AZ_CRC_CE("AZLuaUserData"), "this isn't user data");
|
|
// Lua: LuaUserData::nodeable, class_mt
|
|
lua_newtable(lua);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy
|
|
lua_pushvalue(lua, -2);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, class_mt
|
|
lua_setmetatable(lua, -2);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy
|
|
lua_newtable(lua);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt
|
|
lua_pushvalue(lua, -2);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt, proxy
|
|
lua_setfield(lua, -2, "__index");
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt
|
|
lua_pushvalue(lua, -2);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt, proxy
|
|
lua_pushcclosure(lua, &ProxyNewIndexMethod, 1);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt, ProxyNewIndexMethod
|
|
lua_setfield(lua, -2, "__newindex");
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt
|
|
lua_pushcfunction(lua, &DeleteNodeable);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt, DeleteNodeable
|
|
lua_setfield(lua, -2, "__gc");
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy, userdata_mt
|
|
lua_setmetatable(lua, -4);
|
|
// Lua: LuaUserData::nodeable, class_mt, proxy
|
|
lua_pop(lua, 2);
|
|
// Lua: LuaUserData::nodeable
|
|
AZ_Assert(reinterpret_cast<AZ::LuaUserData*>(lua_touserdata(lua, -1)) == userData, "must leave original userdata at the top of the stack");
|
|
return 1;
|
|
}
|
|
|
|
void PushActivationArgs(lua_State* lua, AZ::BehaviorValueParameter* arguments, size_t numArguments)
|
|
{
|
|
auto behaviorContext = AZ::ScriptContext::FromNativeContext(lua)->GetBoundContext();
|
|
|
|
for (size_t i = 0; i < numArguments; ++i)
|
|
{
|
|
Execution::StackPush(lua, behaviorContext, arguments[i]);
|
|
}
|
|
}
|
|
|
|
int GetRandomSwitchControlNumber(lua_State* lua)
|
|
{
|
|
lua_pushnumber(lua, MathNodeUtilities::GetRandom(lua_Number(0), lua_tonumber(lua, -1)));
|
|
return 1;
|
|
}
|
|
|
|
void RegisterAPI(lua_State* lua)
|
|
{
|
|
using namespace ScriptCanvas::Grammar;
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
|
|
lua_register(lua, k_InitializeNodeableOutKeys, &InitializeNodeableOutKeys);
|
|
lua_register(lua, k_NodeableCallInterpretedOut, &CallExecutionOut);
|
|
lua_register(lua, k_NodeableSetExecutionOutName, &SetExecutionOut);
|
|
lua_register(lua, k_NodeableSetExecutionOutResultName, &SetExecutionOutResult);
|
|
lua_register(lua, k_NodeableSetExecutionOutUserSubgraphName, &SetExecutionOutUserSubgraph);
|
|
lua_register(lua, k_OverrideNodeableMetatableName, &OverrideNodeableMetatable);
|
|
lua_register(lua, k_UnpackDependencyConstructionArgsFunctionName, &UnpackDependencyConstructionArgs);
|
|
lua_register(lua, k_UnpackDependencyConstructionArgsLeafFunctionName, &UnpackDependencyConstructionArgsLeaf);
|
|
|
|
#if defined(_RELEASE)
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, k_InterpretedConfigurationPerformance);
|
|
lua_pushboolean(lua, true);
|
|
lua_setglobal(lua, k_InterpretedConfigurationRelease);
|
|
#else
|
|
// all others default to debug configuration
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, k_InterpretedConfigurationPerformance);
|
|
lua_pushboolean(lua, false);
|
|
lua_setglobal(lua, k_InterpretedConfigurationRelease);
|
|
#endif
|
|
|
|
lua_register(lua, k_GetRandomSwitchControlNumberName, &GetRandomSwitchControlNumber);
|
|
|
|
RegisterTypeSafeEBusResultFunctions(lua);
|
|
RegisterCloningAPI(lua);
|
|
RegisterDebugAPI(lua);
|
|
RegisterEBusHandlerAPI(lua);
|
|
lua_gc(lua, LUA_GCCOLLECT, 0);
|
|
}
|
|
|
|
int CallExecutionOut(lua_State* lua)
|
|
{
|
|
// Lua: executionState, outKey, args...
|
|
const int argsCount = lua_gettop(lua);
|
|
AZ_Assert(argsCount >= 2, "CallExecutionOut: Error in compiled Lua file, not enough arguments");
|
|
AZ_Assert(lua_isuserdata(lua, 1), "CallExecutionOut: Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
|
|
AZ_Assert(lua_isnumber(lua, 2), "CallExecutionOut: Error in compiled lua file, 2nd argument to SetExecutionOut is not a number");
|
|
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, 1);
|
|
size_t index = aznumeric_caster(lua_tointeger(lua, 2));
|
|
nodeable->CallOut(index, nullptr, nullptr, argsCount - 2);
|
|
// Lua: results...
|
|
return lua_gettop(lua);
|
|
}
|
|
|
|
void InitializeInterpretedStatics(const RuntimeData& runtimeData)
|
|
{
|
|
#if defined(PROFILE) || defined(AZ_DEBUG_BUILD)
|
|
Execution::InitializeFromLuaStackFunctions(const_cast<Grammar::DebugSymbolMap&>(runtimeData.m_debugMap));
|
|
#endif
|
|
if (runtimeData.RequiresStaticInitialization())
|
|
{
|
|
AZ::ScriptLoadResult result{};
|
|
AZ::ScriptSystemRequestBus::BroadcastResult(result, &AZ::ScriptSystemRequests::LoadAndGetNativeContext, runtimeData.m_script, AZ::k_scriptLoadBinary, AZ::ScriptContextIds::DefaultScriptContextId);
|
|
AZ_Assert(result.status == AZ::ScriptLoadResult::Status::Initial, "ExecutionStateInterpreted script asset was valid but failed to load.");
|
|
AZ_Assert(result.lua, "Must have a default script context and a lua_State");
|
|
AZ_Assert(lua_istable(result.lua, -1), "No run-time execution was available for this script");
|
|
|
|
auto lua = result.lua;
|
|
// Lua: table
|
|
lua_getfield(lua, -1, Grammar::k_InitializeStaticsName);
|
|
// Lua: table, ?
|
|
if (lua_isfunction(lua, -1))
|
|
{
|
|
// Lua: table, function
|
|
lua_pushvalue(lua, -2);
|
|
// Lua: table, function, table
|
|
for (auto& clonerSource : runtimeData.m_cloneSources)
|
|
{
|
|
lua_pushlightuserdata(lua, const_cast<void*>(reinterpret_cast<const void*>(&clonerSource)));
|
|
}
|
|
// Lua: table, function, table, cloners...
|
|
AZ::Internal::LuaSafeCall(lua, aznumeric_caster(runtimeData.m_cloneSources.size() + 1), 0);
|
|
// Lua: table
|
|
lua_pop(lua, 1);
|
|
}
|
|
else
|
|
{
|
|
// Lua: table, ?
|
|
lua_pop(lua, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
int InitializeNodeableOutKeys(lua_State* lua)
|
|
{
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
// Lua: usernodeable, keyCount
|
|
[[maybe_unused]] const int argsCount = lua_gettop(lua);
|
|
AZ_Assert(argsCount == 2, "InitializeNodeableOutKeys: Error in compiled Lua file, not enough arguments");
|
|
AZ_Assert(lua_isuserdata(lua, 1), "InitializeNodeableOutKeys: Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
|
|
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, 1);
|
|
AZ_Assert(lua_isnumber(lua, 2), "InitializeNodeableOutKeys: Error in compiled lua file, 2nd argument was not an integer");
|
|
const size_t keyCount = aznumeric_caster(lua_tointeger(lua, 2));
|
|
nodeable->InitializeExecutionOuts(keyCount);
|
|
return 0;
|
|
}
|
|
|
|
int InterpretedSafeCall(lua_State* lua, int argCount, int returnValueCount)
|
|
{
|
|
const int handlerIndex = lua_gettop(lua) - argCount;
|
|
lua_pushcfunction(lua, &ExecutionInterpretedAPICpp::ErrorHandler);
|
|
lua_insert(lua, handlerIndex);
|
|
int result = lua_pcall(lua, argCount, returnValueCount, handlerIndex);
|
|
lua_remove(lua, handlerIndex);
|
|
return result;
|
|
}
|
|
|
|
int SetExecutionOut(lua_State* lua)
|
|
{
|
|
// \note Return values could become necessary.
|
|
// \see LY-99750
|
|
|
|
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOut is not userdata (Nodeable)");
|
|
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOut is not a number");
|
|
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOut is not a function (lambda need to get around atypically routed arguments)");
|
|
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3);
|
|
AZ_Assert(nodeable, "Failed to read nodeable");
|
|
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
lua_pushvalue(lua, -1);
|
|
// Lua: nodeable, index, lambda, lambda
|
|
|
|
nodeable->SetExecutionOut(index, OutInterpreted(lua));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
// \todo clear these immediately after they are not needed with an explicit call written by the translator
|
|
return 0;
|
|
}
|
|
|
|
int SetExecutionOutResult(lua_State* lua)
|
|
{
|
|
// \note Return values could become necessary.
|
|
// \see LY-99750
|
|
|
|
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOutResult is not userdata (Nodeable)");
|
|
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOutResult is not a number");
|
|
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOutResult is not a function (lambda need to get around atypically routed arguments)");
|
|
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3); // this won't be a BCO, because BCOs won't be necessary in the interpreted mode...most likely
|
|
AZ_Assert(nodeable, "Failed to read nodeable");
|
|
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
lua_pushvalue(lua, -1);
|
|
// Lua: nodeable, index, lambda, lambda
|
|
|
|
nodeable->SetExecutionOut(index, OutInterpretedResult(lua));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
// \todo clear these immediately after they are not needed with an explicit call written by the translator
|
|
return 0;
|
|
}
|
|
|
|
int SetExecutionOutUserSubgraph(lua_State* lua)
|
|
{
|
|
AZ_Assert(lua_isuserdata(lua, -3), "Error in compiled lua file, 1st argument to SetExecutionOutUserSubgraph is not userdata (Nodeable)");
|
|
AZ_Assert(lua_isnumber(lua, -2), "Error in compiled lua file, 2nd argument to SetExecutionOutUserSubgraph is not a number");
|
|
AZ_Assert(lua_isfunction(lua, -1), "Error in compiled lua file, 3rd argument to SetExecutionOutUserSubgraph is not a function (lambda need to get around atypically routed arguments)");
|
|
Nodeable* nodeable = AZ::ScriptValue<Nodeable*>::StackRead(lua, -3);
|
|
AZ_Assert(nodeable, "Failed to read nodeable");
|
|
size_t index = aznumeric_caster(lua_tointeger(lua, -2));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
lua_pushvalue(lua, -1);
|
|
// Lua: nodeable, index, lambda, lambda
|
|
|
|
nodeable->SetExecutionOut(index, OutInterpretedUserSubgraph(lua));
|
|
// Lua: nodeable, index, lambda
|
|
|
|
// \todo clear these immediately after they are not needed with an explicit call written by the translator
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// \todo ScriptCanvas will probably need its own version of all of these functions
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void StackPush(lua_State* lua, AZ::BehaviorContext* context, AZ::BehaviorValueParameter& argument)
|
|
{
|
|
using namespace ExecutionInterpretedAPICpp;
|
|
|
|
if (auto valuePtr = GetAs<const char*>(argument))
|
|
{
|
|
auto realValue = reinterpret_cast<const char*>(valuePtr);
|
|
lua_pushstring(lua, realValue);
|
|
}
|
|
else if (auto valuePtr2 = GetAs<AZStd::string>(argument))
|
|
{
|
|
lua_pushlstring(lua, valuePtr2->data(), valuePtr2->size());
|
|
}
|
|
else if (auto valuePtr3 = GetAs<AZStd::string_view>(argument))
|
|
{
|
|
lua_pushlstring(lua, valuePtr3->data(), valuePtr3->size());
|
|
}
|
|
else
|
|
{
|
|
// \todo determine whether or not to adjust this for return results
|
|
AZ::StackPush(lua, context, argument);
|
|
}
|
|
}
|
|
|
|
bool StackRead(lua_State* lua, AZ::BehaviorContext* context, int index, AZ::BehaviorValueParameter& param, AZ::StackVariableAllocator* allocator)
|
|
{
|
|
return AZ::StackRead(lua, index, context, param, allocator);
|
|
}
|
|
|
|
void InterpretedUnloadData(RuntimeData& runtimeData)
|
|
{
|
|
AZ::ScriptSystemRequestBus::Broadcast(&AZ::ScriptSystemRequests::ClearAssetReferences, runtimeData.m_script.GetId());
|
|
}
|
|
|
|
struct DependencyConstructionPack
|
|
{
|
|
ExecutionStateInterpreted* executionState;
|
|
AZStd::vector<RuntimeDataOverrides>* dependencies;
|
|
const size_t dependenciesIndex;
|
|
RuntimeDataOverrides& runtimeOverrides;
|
|
};
|
|
|
|
DependencyConstructionPack UnpackDependencyConstructionArgsSanitize(lua_State* lua)
|
|
{
|
|
auto executionState = AZ::ScriptValue<ExecutionStateInterpreted*>::StackRead(lua, 1);
|
|
AZ_Assert(executionState, "Error in compiled lua file, 1st argument to UnpackDependencyArgs is not an ExecutionStateInterpreted");
|
|
AZ_Assert(lua_islightuserdata(lua, 2), "Error in compiled lua file, 2nd argument to UnpackDependencyArgs is not userdata (AZStd::vector<AZ::Data::Asset<RuntimeAsset>>*), but a :%s", lua_typename(lua, 2));
|
|
auto dependentOverrides = reinterpret_cast<AZStd::vector<RuntimeDataOverrides>*>(lua_touserdata(lua, 2));
|
|
AZ_Assert(lua_isinteger(lua, 3), "Error in compiled Lua file, 3rd argument to UnpackDependencyArgs is not a number");
|
|
const size_t dependencyIndex = aznumeric_caster(lua_tointeger(lua, 3));
|
|
return DependencyConstructionPack{ executionState, dependentOverrides, dependencyIndex, (*dependentOverrides)[dependencyIndex] };
|
|
}
|
|
|
|
int Unpack(lua_State* lua, DependencyConstructionPack& args)
|
|
{
|
|
ActivationInputArray storage;
|
|
ActivationData data(args.runtimeOverrides, storage);
|
|
ActivationInputRange range = Execution::Context::CreateActivateInputRange(data, args.executionState->GetEntityId());
|
|
PushActivationArgs(lua, range.inputs, range.totalCount);
|
|
return static_cast<int>(range.totalCount);
|
|
}
|
|
|
|
int UnpackDependencyConstructionArgs(lua_State* lua)
|
|
{
|
|
// Lua: executionState, dependent overrides, index into dependent overrides
|
|
DependencyConstructionPack pack = UnpackDependencyConstructionArgsSanitize(lua);
|
|
lua_pushlightuserdata(lua, const_cast<void*>(reinterpret_cast<const void*>(&pack.runtimeOverrides.m_dependencies)));
|
|
return 1 + Unpack(lua, pack);
|
|
}
|
|
|
|
int UnpackDependencyConstructionArgsLeaf(lua_State* lua)
|
|
{
|
|
// Lua: executionState, dependentAssets, dependentAssetsIndex
|
|
DependencyConstructionPack constructionArgs = UnpackDependencyConstructionArgsSanitize(lua);
|
|
return Unpack(lua, constructionArgs);
|
|
}
|
|
}
|
|
}
|