From db4f8f344cbc8cbdbfb9d37f5b7f0e8919ec99b0 Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 00:43:59 +0300
Subject: [PATCH 1/8] Check the type of the passed context for RTTI function
---
src/reverse/RTTIHelper.cpp | 63 +++++++++++++++++++++++++++++++++++---
src/reverse/RTTIHelper.h | 4 ++-
2 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/src/reverse/RTTIHelper.cpp b/src/reverse/RTTIHelper.cpp
index 0e7e559e..42545928 100644
--- a/src/reverse/RTTIHelper.cpp
+++ b/src/reverse/RTTIHelper.cpp
@@ -5,6 +5,7 @@
#include "Type.h"
#include "ClassReference.h"
#include "StrongReference.h"
+#include "WeakReference.h"
#include "scripting/Scripting.h"
#include "Utils.h"
#include
@@ -114,6 +115,39 @@ void RTTIHelper::AddFunctionAlias(const std::string& acAliasClassName, const std
}
}
+bool RTTIHelper::IsFunctionAlias(RED4ext::CBaseFunction* apFunc)
+{
+ static const auto s_cTweakDBInterfaceHash = RED4ext::FNV1a("gamedataTweakDBInterface");
+
+ if (m_extendedFunctions.contains(kGlobalHash))
+ {
+ auto& extendedFuncs = m_extendedFunctions.at(kGlobalHash);
+
+ if (extendedFuncs.contains(apFunc->fullName.hash))
+ return true;
+ }
+
+ if (apFunc->GetParent())
+ {
+ const auto cClassHash = apFunc->GetParent()->name.hash;
+
+ // TweakDBInterface is special.
+ // All of its methods are non-static, but they can only be used as static ones.
+ if (cClassHash == s_cTweakDBInterfaceHash)
+ return true;
+
+ if (m_extendedFunctions.contains(cClassHash))
+ {
+ auto& extendedFuncs = m_extendedFunctions.at(cClassHash);
+
+ if (extendedFuncs.contains(apFunc->fullName.hash))
+ return true;
+ }
+ }
+
+ return false;
+}
+
sol::function RTTIHelper::GetResolvedFunction(const uint64_t acFuncHash) const
{
return GetResolvedFunction(kGlobalHash, acFuncHash, false);
@@ -373,12 +407,14 @@ sol::function RTTIHelper::MakeInvokableFunction(RED4ext::CBaseFunction* apFunc)
auto lockedState = m_lua.Lock();
auto& luaState = lockedState.Get();
- return MakeSolFunction(luaState, [this, apFunc](sol::variadic_args aArgs, sol::this_state aState, sol::this_environment aEnv) -> sol::variadic_results {
+ const bool cAllowNull = IsFunctionAlias(apFunc);
+
+ return MakeSolFunction(luaState, [this, apFunc, cAllowNull](sol::variadic_args aArgs, sol::this_state aState, sol::this_environment aEnv) -> sol::variadic_results {
uint64_t argOffset = 0;
RED4ext::ScriptInstance pHandle = ResolveHandle(apFunc, aArgs, argOffset);
std::string errorMessage;
- auto result = ExecuteFunction(apFunc, pHandle, aArgs, argOffset, errorMessage);
+ auto result = ExecuteFunction(apFunc, pHandle, aArgs, argOffset, errorMessage, cAllowNull);
if (!errorMessage.empty())
{
@@ -501,10 +537,19 @@ RED4ext::ScriptInstance RTTIHelper::ResolveHandle(RED4ext::CBaseFunction* apFunc
if (cArg.is())
{
- pHandle = cArg.as().GetHandle();
+ pHandle = cArg.as()->GetHandle();
+
+ if (cArg.is() || cArg.is())
+ {
+ ++aArgOffset;
- if (pHandle || cArg.is())
+ if (pHandle && !reinterpret_cast(pHandle)->GetType()->IsA(apFunc->GetParent()))
+ pHandle = nullptr;
+ }
+ else if (cArg.is())
+ {
++aArgOffset;
+ }
}
}
}
@@ -514,7 +559,7 @@ RED4ext::ScriptInstance RTTIHelper::ResolveHandle(RED4ext::CBaseFunction* apFunc
sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc, RED4ext::ScriptInstance apHandle,
sol::variadic_args aLuaArgs, uint64_t aLuaArgOffset,
- std::string& aErrorMessage) const
+ std::string& aErrorMessage, bool aAllowNull) const
{
static thread_local TiltedPhoques::ScratchAllocator s_scratchMemory(1 << 14);
static thread_local uint32_t s_callDepth = 0u;
@@ -533,6 +578,14 @@ sol::variadic_results RTTIHelper::ExecuteFunction(RED4ext::CBaseFunction* apFunc
// args is implemented (should check if a compatible arg is actually
// passed at the expected position + same for optionals).
+ if (!apFunc->flags.isStatic && !apHandle && !aAllowNull)
+ {
+ aErrorMessage = fmt::format("Function '{}' context must be '{}'.",
+ apFunc->shortName.ToString(),
+ apFunc->GetParent()->name.ToString());
+ return {};
+ }
+
auto numArgs = aLuaArgs.size() - aLuaArgOffset;
auto minArgs = 0u;
auto maxArgs = 0u;
diff --git a/src/reverse/RTTIHelper.h b/src/reverse/RTTIHelper.h
index 424507b6..5ef1bc7a 100644
--- a/src/reverse/RTTIHelper.h
+++ b/src/reverse/RTTIHelper.h
@@ -20,7 +20,7 @@ struct RTTIHelper
RED4ext::ScriptInstance ResolveHandle(RED4ext::CBaseFunction* apFunc, sol::variadic_args& aArgs, uint64_t& aArgOffset) const;
sol::variadic_results ExecuteFunction(RED4ext::CBaseFunction* apFunc, RED4ext::ScriptInstance apHandle,
sol::variadic_args aLuaArgs, uint64_t aLuaArgOffset,
- std::string& aErrorMessage) const;
+ std::string& aErrorMessage, bool aAllowNull = false) const;
RED4ext::ScriptInstance NewInstance(RED4ext::CBaseRTTIType* apType, sol::optional aProps,
TiltedPhoques::Allocator* apAllocator) const;
@@ -46,6 +46,8 @@ struct RTTIHelper
void InitializeRTTI();
void ParseGlobalStatics();
+ bool IsFunctionAlias(RED4ext::CBaseFunction* apFunc);
+
sol::function GetResolvedFunction(const uint64_t acFuncHash) const;
sol::function GetResolvedFunction(const uint64_t acClassHash, const uint64_t acFuncHash, bool aIsMember) const;
void AddResolvedFunction(const uint64_t acFuncHash, sol::function& acFunc);
From a24c109d84b932a4d0a42a4cf00cdbd611b99ebb Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 00:52:20 +0300
Subject: [PATCH 2/8] Add ISerializable references support
---
src/reverse/StrongReference.cpp | 17 +++++++++++++++++
src/reverse/StrongReference.h | 3 +++
src/reverse/WeakReference.cpp | 18 ++++++++++++++++++
src/reverse/WeakReference.h | 3 +++
src/scripting/Scripting.cpp | 4 ++--
5 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/reverse/StrongReference.cpp b/src/reverse/StrongReference.cpp
index 1ed3e9f7..53089c6d 100644
--- a/src/reverse/StrongReference.cpp
+++ b/src/reverse/StrongReference.cpp
@@ -1,9 +1,12 @@
#include
#include "StrongReference.h"
+#include "RTTILocator.h"
#include "CET.h"
+static RTTILocator s_sIScriptableType{RED4ext::FNV1a("IScriptable")};
+
StrongReference::StrongReference(const TiltedPhoques::Lockable::Ref& aView,
RED4ext::Handle aStrongHandle)
: ClassType(aView, nullptr)
@@ -15,6 +18,20 @@ StrongReference::StrongReference(const TiltedPhoques::Lockable::Ref& aView,
+ RED4ext::Handle aStrongHandle,
+ RED4ext::CHandle* apStrongHandleType)
+ : ClassType(aView, nullptr)
+ , m_strongHandle(std::move(aStrongHandle))
+{
+ if (m_strongHandle)
+ {
+ auto const cpClass = reinterpret_cast(apStrongHandleType->GetInnerType());
+
+ m_pType = cpClass->IsA(s_sIScriptableType) ? m_strongHandle->GetType() : cpClass;
+ }
+}
+
StrongReference::~StrongReference()
{
// Nasty hack so that the Lua VM doesn't try to release game memory on shutdown
diff --git a/src/reverse/StrongReference.h b/src/reverse/StrongReference.h
index e12d541b..f03f104c 100644
--- a/src/reverse/StrongReference.h
+++ b/src/reverse/StrongReference.h
@@ -6,6 +6,9 @@ struct StrongReference : ClassType
{
StrongReference(const TiltedPhoques::Lockable::Ref& aView,
RED4ext::Handle aStrongHandle);
+ StrongReference(const TiltedPhoques::Lockable::Ref& aView,
+ RED4ext::Handle aStrongHandle,
+ RED4ext::CHandle* apStrongHandleType);
virtual ~StrongReference();
protected:
diff --git a/src/reverse/WeakReference.cpp b/src/reverse/WeakReference.cpp
index b74380e9..b6abc456 100644
--- a/src/reverse/WeakReference.cpp
+++ b/src/reverse/WeakReference.cpp
@@ -1,9 +1,12 @@
#include
#include "WeakReference.h"
+#include "RTTILocator.h"
#include "CET.h"
+static RTTILocator s_sIScriptableType{RED4ext::FNV1a("IScriptable")};
+
WeakReference::WeakReference(const TiltedPhoques::Lockable::Ref& aView,
RED4ext::WeakHandle aWeakHandle)
: ClassType(aView, nullptr)
@@ -16,6 +19,21 @@ WeakReference::WeakReference(const TiltedPhoques::Lockable::Ref& aView,
+ RED4ext::WeakHandle aWeakHandle,
+ RED4ext::CWeakHandle* apWeakHandleType)
+ : ClassType(aView, nullptr)
+ , m_weakHandle(std::move(aWeakHandle))
+{
+ auto ref = m_weakHandle.Lock();
+ if (ref)
+ {
+ auto const cpClass = reinterpret_cast(apWeakHandleType->GetInnerType());
+
+ m_pType = cpClass->IsA(s_sIScriptableType) ? ref->GetType() : cpClass;
+ }
+}
+
WeakReference::~WeakReference()
{
// Nasty hack so that the Lua VM doesn't try to release game memory on shutdown
diff --git a/src/reverse/WeakReference.h b/src/reverse/WeakReference.h
index 31524216..ff0ca564 100644
--- a/src/reverse/WeakReference.h
+++ b/src/reverse/WeakReference.h
@@ -6,6 +6,9 @@ struct WeakReference : ClassType
{
WeakReference(const TiltedPhoques::Lockable::Ref& aView,
RED4ext::WeakHandle aWeakHandle);
+ WeakReference(const TiltedPhoques::Lockable::Ref& aView,
+ RED4ext::WeakHandle aWeakHandle,
+ RED4ext::CWeakHandle* apWeakHandleType);
virtual ~WeakReference();
protected:
diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp
index 5a96edae..57d01bf0 100644
--- a/src/scripting/Scripting.cpp
+++ b/src/scripting/Scripting.cpp
@@ -712,13 +712,13 @@ sol::object Scripting::ToLua(LockedState& aState, RED4ext::CStackType& aResult)
{
const auto handle = *static_cast*>(aResult.value);
if (handle)
- return make_object(state, StrongReference(aState, handle));
+ return make_object(state, StrongReference(aState, handle, static_cast(pType)));
}
else if (pType->GetType() == RED4ext::ERTTIType::WeakHandle)
{
const auto handle = *static_cast*>(aResult.value);
if (!handle.Expired())
- return make_object(state, WeakReference(aState, handle));
+ return make_object(state, WeakReference(aState, handle, static_cast(pType)));
}
else if (pType->GetType() == RED4ext::ERTTIType::Array)
{
From ac0290af42183eba6fbd7d964aa28e632ce3089d Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 00:54:15 +0300
Subject: [PATCH 3/8] Always call all function observers
---
src/scripting/FunctionOverride.cpp | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/scripting/FunctionOverride.cpp b/src/scripting/FunctionOverride.cpp
index 073ab0d2..c5288763 100644
--- a/src/scripting/FunctionOverride.cpp
+++ b/src/scripting/FunctionOverride.cpp
@@ -26,7 +26,7 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc
return false;
}
- bool isOverride = false;
+ bool isOverridden = false;
if (!itor->second.Calls.empty())
{
@@ -66,6 +66,9 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc
const auto& calls = itor->second.Calls;
for (const auto& call : calls)
{
+ if (!call->Forward && isOverridden)
+ continue;
+
const auto result = call->ScriptFunction(as_args(args));
if (!result.valid())
@@ -79,8 +82,7 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc
if (result.valid() && ret.value && ret.type)
Scripting::ToRED(result.get(), &ret);
- isOverride = true;
- break;
+ isOverridden = true;
}
}
}
@@ -88,7 +90,7 @@ bool FunctionOverride::HookRunPureScriptFunction(RED4ext::CClassFunction* apFunc
if (itor->second.CollectGarbage)
s_pOverride->m_pScripting->CollectGarbage();
- if (isOverride)
+ if (isOverridden)
return true;
auto* pTrampoline = itor->second.Trampoline;
@@ -196,7 +198,7 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext,
}
const auto& context = itor->second;
- bool isOverride = false;
+ bool isOverridden = false;
// Save state so we can rollback to it after we popped for ourself
auto* pCode = apFrame->code;
@@ -283,6 +285,9 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext,
const auto& calls = context.Calls;
for (const auto& call : calls)
{
+ if (!call->Forward && isOverridden)
+ continue;
+
const auto result = call->ScriptFunction(as_args(args));
if (!result.valid())
@@ -303,8 +308,7 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext,
Scripting::ToRED(result.get(), &redResult);
}
- isOverride = true;
- break;
+ isOverridden = true;
}
}
}
@@ -312,7 +316,7 @@ void FunctionOverride::HandleOverridenFunction(RED4ext::IScriptable* apContext,
if (context.CollectGarbage)
s_pOverride->m_pScripting->CollectGarbage();
- if (isOverride)
+ if (isOverridden)
return;
RED4ext::IFunction* pRealFunction = context.Trampoline;
From 928c49f706c2555831beb23d7aca5cdb43a44d04 Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 01:55:54 +0300
Subject: [PATCH 4/8] Add ClassType to Lua type hierarchy
---
src/reverse/Type.h | 2 ++
src/scripting/Scripting.cpp | 16 +++++++++++-----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/reverse/Type.h b/src/reverse/Type.h
index 433eed2c..9d796299 100644
--- a/src/reverse/Type.h
+++ b/src/reverse/Type.h
@@ -49,6 +49,8 @@ struct ClassType : Type
Descriptor Dump(bool aWithHashes) const override;
sol::object Index_Impl(const std::string& acName, sol::this_environment aThisEnv) override;
sol::object NewIndex_Impl(const std::string& acName, sol::object aParam) override;
+
+ RED4ext::CClass* GetClass() const { return reinterpret_cast(m_pType); };
};
struct UnknownType : Type
diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp
index 57d01bf0..58a52268 100644
--- a/src/scripting/Scripting.cpp
+++ b/src/scripting/Scripting.cpp
@@ -132,36 +132,42 @@ void Scripting::PostInitialize()
sol::meta_function::index, &Type::Index,
sol::meta_function::new_index, &Type::NewIndex);
+ luaVm.new_usertype("__ClassType",
+ sol::meta_function::construct, sol::no_constructor,
+ sol::base_classes, sol::bases(),
+ sol::meta_function::index, &ClassType::Index,
+ sol::meta_function::new_index, &ClassType::NewIndex);
+
luaVm.new_usertype("Descriptor",
sol::meta_function::to_string, &Type::Descriptor::ToString);
luaVm.new_usertype("StrongReference",
sol::meta_function::construct, sol::no_constructor,
- sol::base_classes, sol::bases(),
+ sol::base_classes, sol::bases(),
sol::meta_function::index, &StrongReference::Index,
sol::meta_function::new_index, &StrongReference::NewIndex);
luaVm.new_usertype("WeakReference",
sol::meta_function::construct, sol::no_constructor,
- sol::base_classes, sol::bases(),
+ sol::base_classes, sol::bases(),
sol::meta_function::index, &WeakReference::Index,
sol::meta_function::new_index, &WeakReference::NewIndex);
luaVm.new_usertype("SingletonReference",
sol::meta_function::construct, sol::no_constructor,
- sol::base_classes, sol::bases(),
+ sol::base_classes, sol::bases(),
sol::meta_function::index, &SingletonReference::Index,
sol::meta_function::new_index, &SingletonReference::NewIndex);
luaVm.new_usertype("ClassReference",
sol::meta_function::construct, sol::no_constructor,
- sol::base_classes, sol::bases(),
+ sol::base_classes, sol::bases(),
sol::meta_function::index, &ClassReference::Index,
sol::meta_function::new_index, &ClassReference::NewIndex);
luaVm.new_usertype("ResourceAsyncReference",
sol::meta_function::construct, sol::no_constructor,
- sol::base_classes, sol::bases(),
+ sol::base_classes, sol::bases(),
sol::meta_function::index, &ResourceAsyncReference::Index,
sol::meta_function::new_index, &ResourceAsyncReference::NewIndex,
"hash", sol::property(&ResourceAsyncReference::GetLuaHash));
From b4a4faf55218191c429c037143b5df838bed77aa Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 01:56:42 +0300
Subject: [PATCH 5/8] Disable implicit Lua table to struct conversion
---
src/reverse/Converter.h | 34 ++++++++++++++++++----------------
1 file changed, 18 insertions(+), 16 deletions(-)
diff --git a/src/reverse/Converter.h b/src/reverse/Converter.h
index e35414c6..b16c75cc 100644
--- a/src/reverse/Converter.h
+++ b/src/reverse/Converter.h
@@ -210,23 +210,25 @@ struct ClassConverter : LuaRED
}
else if (aObject == sol::nil)
{
- result.value = RTTIHelper::Get().NewInstance(apRtti, sol::nullopt, apAllocator);
- }
- else if (aObject.get_type() == sol::type::table)
- {
- // The implicit table to instance conversion `Game.FindEntityByID({ hash = 1 })` has potential issue:
- // When the overloaded function takes an array and an object for the same arg the implicit conversion
- // can produce an empty instance making the unwanted overload callable. So for better experience it's
- // important to distinguish between linear array and array of props.
-
- // Size check excludes non-empty linear arrays since only the table with sequential and integral keys
- // has size (length). And iterator check excludes empty tables `{}`.
- sol::table props = aObject.as();
- if (props.size() == 0 && props.begin() != props.end())
- result.value = RTTIHelper::Get().NewInstance(apRtti, props, apAllocator);
- else
- result.value = nullptr;
+ result.value = RTTIHelper::Get().NewInstance(apRtti, apAllocator);
}
+ // Disabled until new allocator is implemented
+ // Current implementation can leak
+ //else if (aObject.get_type() == sol::type::table)
+ //{
+ // // The implicit table to instance conversion `Game.FindEntityByID({ hash = 1 })` has potential issue:
+ // // When the overloaded function takes an array and an object for the same arg the implicit conversion
+ // // can produce an empty instance making the unwanted overload callable. So for better experience it's
+ // // important to distinguish between linear array and array of props.
+ //
+ // // Size check excludes non-empty linear arrays since only the table with sequential and integral keys
+ // // has size (length). And iterator check excludes empty tables `{}`.
+ // sol::table props = aObject.as();
+ // if (props.size() == 0 && props.begin() != props.end())
+ // result.value = RTTIHelper::Get().NewInstance(apRtti, props, apAllocator);
+ // else
+ // result.value = nullptr;
+ //}
else
{
result.value = nullptr;
From 0a6fdebc7871b259f873aa41075679d355e102b6 Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 05:28:41 +0300
Subject: [PATCH 6/8] Workaround for parameterized struct constructor
---
src/reverse/Converter.h | 2 +-
src/reverse/RTTIHelper.cpp | 33 +++++++++++++++++++++++----------
src/reverse/RTTIHelper.h | 1 +
3 files changed, 25 insertions(+), 11 deletions(-)
diff --git a/src/reverse/Converter.h b/src/reverse/Converter.h
index b16c75cc..43eaa44c 100644
--- a/src/reverse/Converter.h
+++ b/src/reverse/Converter.h
@@ -210,7 +210,7 @@ struct ClassConverter : LuaRED
}
else if (aObject == sol::nil)
{
- result.value = RTTIHelper::Get().NewInstance(apRtti, apAllocator);
+ result.value = RTTIHelper::Get().NewInstance(apRtti, sol::nullopt, apAllocator);
}
// Disabled until new allocator is implemented
// Current implementation can leak
diff --git a/src/reverse/RTTIHelper.cpp b/src/reverse/RTTIHelper.cpp
index 42545928..97f703de 100644
--- a/src/reverse/RTTIHelper.cpp
+++ b/src/reverse/RTTIHelper.cpp
@@ -791,11 +791,7 @@ RED4ext::ScriptInstance RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType,
auto* pInstance = pClass->AllocInstance();
if (aProps.has_value())
- {
- bool success;
- for (const auto& cProp : aProps.value())
- SetProperty(pClass, pInstance, cProp.first.as(), cProp.second, success);
- }
+ SetProperties(pClass, pInstance, aProps.value());
if (apType->GetType() == RED4ext::ERTTIType::Handle)
return apAllocator->New>((RED4ext::IScriptable*)pInstance);
@@ -812,12 +808,18 @@ sol::object RTTIHelper::NewInstance(RED4ext::CBaseRTTIType* apType, sol::optiona
RED4ext::CStackType result;
result.type = apType;
- result.value = NewInstance(apType, aProps, &allocator);
+ result.value = NewInstance(apType, sol::nullopt, &allocator);
auto lockedState = m_lua.Lock();
auto instance = Scripting::ToLua(lockedState, result);
FreeInstance(result, true, true, &allocator);
+
+ if (aProps.has_value())
+ {
+ const auto pInstance = instance.as();
+ SetProperties(pInstance->GetClass(), pInstance->GetHandle(), aProps);
+ }
return instance;
}
@@ -829,9 +831,6 @@ sol::object RTTIHelper::NewHandle(RED4ext::CBaseRTTIType* apType, sol::optional<
// The behavior is similar to what can be seen in scripts, where variables of IScriptable
// types are declared with the ref<> modifier (which means Handle<>).
- // The Handle<> wrapper prevents memory leaks that can occur in IRTTIType::Assign()
- // when called directly on an IScriptable instance.
-
if (!m_pRtti)
return sol::nil;
@@ -839,7 +838,7 @@ sol::object RTTIHelper::NewHandle(RED4ext::CBaseRTTIType* apType, sol::optional<
RED4ext::CStackType result;
result.type = apType;
- result.value = NewInstance(apType, aProps, &allocator);
+ result.value = NewInstance(apType, sol::nullopt, &allocator);
// Wrap IScriptable descendants in Handle
if (result.value && apType->GetType() == RED4ext::ERTTIType::Class)
@@ -864,6 +863,12 @@ sol::object RTTIHelper::NewHandle(RED4ext::CBaseRTTIType* apType, sol::optional<
FreeInstance(result, true, true, &allocator);
+ if (aProps.has_value())
+ {
+ const auto pInstance = instance.as();
+ SetProperties(pInstance->GetClass(), pInstance->GetHandle(), aProps);
+ }
+
return instance;
}
@@ -919,6 +924,14 @@ void RTTIHelper::SetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance a
}
}
+void RTTIHelper::SetProperties(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, sol::optional aProps) const
+{
+ bool success;
+
+ for (const auto& cProp : aProps.value())
+ SetProperty(apClass, apHandle, cProp.first.as(), cProp.second, success);
+}
+
// Check if type is implemented using ClassReference
bool RTTIHelper::IsClassReferenceType(RED4ext::CClass* apClass) const
{
diff --git a/src/reverse/RTTIHelper.h b/src/reverse/RTTIHelper.h
index 5ef1bc7a..4e44062e 100644
--- a/src/reverse/RTTIHelper.h
+++ b/src/reverse/RTTIHelper.h
@@ -29,6 +29,7 @@ struct RTTIHelper
sol::object GetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, const std::string& acPropName, bool& aSuccess) const;
void SetProperty(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, const std::string& acPropName, sol::object aPropValue, bool& aSuccess) const;
+ void SetProperties(RED4ext::CClass* apClass, RED4ext::ScriptInstance apHandle, sol::optional aProps) const;
RED4ext::CBaseFunction* FindFunction(const uint64_t acFullNameHash) const;
RED4ext::CBaseFunction* FindFunction(RED4ext::CClass* apClass, const uint64_t acFullNameHash) const;
From 07e51c76549938a2334ce30b48eab3fcad1d0e32 Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 05:39:57 +0300
Subject: [PATCH 7/8] Add new CNames to the pool
---
src/reverse/BasicTypes.cpp | 5 +++++
src/reverse/BasicTypes.h | 2 ++
src/scripting/Scripting.cpp | 3 ++-
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/reverse/BasicTypes.cpp b/src/reverse/BasicTypes.cpp
index 43d75374..f7b30111 100644
--- a/src/reverse/BasicTypes.cpp
+++ b/src/reverse/BasicTypes.cpp
@@ -65,6 +65,11 @@ bool CName::operator==(const CName& acRhs) const noexcept
return hash == acRhs.hash;
}
+void CName::Add(const std::string& aName)
+{
+ RED4ext::CNamePool::Add(aName.c_str());
+}
+
std::string TweakDBID::ToString() const noexcept
{
return fmt::format("ToTweakDBID{{ hash = 0x{0:08X}, length = {1:d} }}", name_hash, name_length);
diff --git a/src/reverse/BasicTypes.h b/src/reverse/BasicTypes.h
index d076852a..da6cadaf 100644
--- a/src/reverse/BasicTypes.h
+++ b/src/reverse/BasicTypes.h
@@ -92,6 +92,8 @@ struct CName
std::string ToString() const noexcept;
bool operator==(const CName& acRhs) const noexcept;
+
+ static void Add(const std::string& aName);
};
#pragma pack(push, 1)
diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp
index 58a52268..bdcbb6ac 100644
--- a/src/scripting/Scripting.cpp
+++ b/src/scripting/Scripting.cpp
@@ -309,7 +309,8 @@ void Scripting::PostInitialize()
sol::meta_function::equal_to, &CName::operator==,
"hash_lo", &CName::hash_lo,
"hash_hi", &CName::hash_hi,
- "value", sol::property(&CName::AsString));
+ "value", sol::property(&CName::AsString),
+ "add", &CName::Add);
luaGlobal["CName"] = luaVm["CName"];
luaGlobal["ToCName"] = [](sol::table table) -> CName
From 3895e23d83111c177ebb85fd06eb12c76b6d6c9a Mon Sep 17 00:00:00 2001
From: pv
Date: Sat, 2 Oct 2021 13:16:17 +0300
Subject: [PATCH 8/8] Put all observers before overrides
---
src/scripting/FunctionOverride.cpp | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/scripting/FunctionOverride.cpp b/src/scripting/FunctionOverride.cpp
index c5288763..ed031c86 100644
--- a/src/scripting/FunctionOverride.cpp
+++ b/src/scripting/FunctionOverride.cpp
@@ -499,6 +499,16 @@ void FunctionOverride::Override(const std::string& acTypeName, const std::string
pContext->Environment = aEnvironment;
pContext->Forward = !aAbsolute;
- pEntry->Calls.emplace_back(std::move(pContext));
+ if (aAbsolute || pEntry->Calls.empty())
+ {
+ pEntry->Calls.emplace_back(std::move(pContext));
+ }
+ else
+ {
+ auto pos = std::find_if(pEntry->Calls.rbegin(), pEntry->Calls.rend(),
+ [](TiltedPhoques::UniquePtr& aContext) { return aContext->Forward; });
+
+ pEntry->Calls.insert(pos.base(), std::move(pContext));
+ }
}
}