diff --git a/shared/sdk/AActor.cpp b/shared/sdk/AActor.cpp index 60ef44a8..e792d35e 100644 --- a/shared/sdk/AActor.cpp +++ b/shared/sdk/AActor.cpp @@ -1,10 +1,17 @@ #include +#include + #include "UObjectArray.hpp" #include "ScriptVector.hpp" #include "ScriptRotator.hpp" #include "UCameraComponent.hpp" #include "TArray.hpp" #include "FMalloc.hpp" +#include "UGameplayStatics.hpp" +#include "UFunction.hpp" +#include "FField.hpp" +#include "FProperty.hpp" +#include "UGameEngine.hpp" #include "AActor.hpp" @@ -139,11 +146,11 @@ struct FTransformUE5 { glm::vec<4, double> Scale3D{1.0, 1.0, 1.0, 1.0}; }; -UActorComponent* AActor::add_component_by_class(UClass* uclass) { +UActorComponent* AActor::add_component_by_class(UClass* uclass, bool deferred) { static const auto func = AActor::static_class()->find_function(L"AddComponentByClass"); if (func == nullptr) { - return nullptr; + return add_component_by_class_ex(uclass, deferred); } std::vector params{}; @@ -171,8 +178,7 @@ UActorComponent* AActor::add_component_by_class(UClass* uclass) { } // Add a bool - bool bDeferredFinish = false; - params.insert(params.end(), (uint8_t*)&bDeferredFinish, (uint8_t*)&bDeferredFinish + sizeof(bool)); + params.insert(params.end(), (uint8_t*)&deferred, (uint8_t*)&deferred + sizeof(bool)); // Align params.insert(params.end(), 7, 0); @@ -188,13 +194,37 @@ UActorComponent* AActor::add_component_by_class(UClass* uclass) { return *(UActorComponent**)(params.data() + ret_offset); } +UActorComponent* AActor::add_component_by_class_ex(UClass* uclass, bool deferred) { + auto ugs = sdk::UGameplayStatics::get(); + + if (ugs == nullptr) { + return nullptr; + } + + auto new_comp = ugs->spawn_object(uclass, this); + + if (new_comp == nullptr) { + return nullptr; + } + + if (!deferred) { + this->finish_add_component(new_comp); + } + + return (UActorComponent*)new_comp; +} + void AActor::finish_add_component(sdk::UObject* component) { static const auto func = AActor::static_class()->find_function(L"FinishAddComponent"); if (func == nullptr) { + finish_add_component_ex(component); return; } + static const auto fvector = sdk::ScriptVector::static_struct(); + const auto is_ue5 = fvector->get_struct_size() == sizeof(glm::vec<3, double>); + /*struct { sdk::UObject* Component{}; // 0x0 bool bManualAttachment{}; // 0x8 @@ -212,9 +242,6 @@ void AActor::finish_add_component(sdk::UObject* component) { params.insert(params.end(), 7, 0); - static const auto fvector = sdk::ScriptVector::static_struct(); - const auto is_ue5 = fvector->get_struct_size() == sizeof(glm::vec<3, double>); - if (!is_ue5) { FTransform transform{}; params.insert(params.end(), (uint8_t*)&transform, (uint8_t*)&transform + sizeof(FTransform)); @@ -222,10 +249,69 @@ void AActor::finish_add_component(sdk::UObject* component) { FTransformUE5 transform{}; params.insert(params.end(), (uint8_t*)&transform, (uint8_t*)&transform + sizeof(FTransformUE5)); } + + /*std::vector params{}; + uint32_t current_offset = 0; + for (auto param = func->get_child_properties(); param != nullptr; param = param->get_next()) { + auto pad_until = [&](uint32_t offset) { + while (current_offset < offset) { + params.push_back(0); + ++current_offset; + } + }; + + const auto param_prop = (sdk::FProperty*)param; + const auto offset = param_prop->get_offset(); + + pad_until(offset); + + switch (utility::hash(utility::narrow(param_prop->get_field_name().to_string()))) { + case "Component"_fnv: + params.insert(params.end(), (uint8_t*)&component, (uint8_t*)&component + sizeof(UObject*)); + current_offset += sizeof(UObject*); + break; + case "RelativeTransform"_fnv: + if (!is_ue5) { + FTransform transform{}; + params.insert(params.end(), (uint8_t*)&transform, (uint8_t*)&transform + sizeof(FTransform)); + } else { + FTransformUE5 transform{}; + params.insert(params.end(), (uint8_t*)&transform, (uint8_t*)&transform + sizeof(FTransformUE5)); + } + + break; + default: + if (param->get_next() == nullptr) { + // add some padding + pad_until(current_offset + 0x40); + } + + break; + }; + }*/ this->process_event(func, params.data()); } +void AActor::finish_add_component_ex(sdk::UObject* new_comp) { + if (new_comp == nullptr) { + return; + } + + // For older UE. + if (new_comp->get_class()->is_a(sdk::USceneComponent::static_class())) { + auto sc = (sdk::USceneComponent*)new_comp; + + if (get_root_component() == nullptr) { + set_root_component(sc); + } else { + sc->attach_to(get_root_component()); + } + } + + ((sdk::UActorComponent*)new_comp)->register_component_with_world(this->get_world()); +} + std::vector AActor::get_components_by_class(UClass* uclass) { static const auto func = AActor::static_class()->find_function(L"K2_GetComponentsByClass"); @@ -293,4 +379,38 @@ void AActor::destroy_actor() { this->process_event(func, ¶ms); } + +USceneComponent* AActor::get_root_component() { + return get_property(L"RootComponent"); +} + +void AActor::set_root_component(USceneComponent* component) { + get_property(L"RootComponent") = component; +} + +UObject* AActor::get_level() { + static const auto func = AActor::static_class()->find_function(L"GetLevel"); + + if (func == nullptr) { + return nullptr; + } + + struct { + UObject* return_value{nullptr}; + } params{}; + + this->process_event(func, ¶ms); + + return params.return_value; +} + +UWorld* AActor::get_world() { + auto level = get_level(); + + if (level == nullptr) { + return nullptr; + } + + return level->get_property(L"OwningWorld"); +} } \ No newline at end of file diff --git a/shared/sdk/AActor.hpp b/shared/sdk/AActor.hpp index 51ba5b87..a268bee6 100644 --- a/shared/sdk/AActor.hpp +++ b/shared/sdk/AActor.hpp @@ -11,6 +11,7 @@ class UCameraComponent; class USceneComponent; class UActorComponent; class AActor; +class UWorld; class AActor : public UObject { public: @@ -25,8 +26,10 @@ class AActor : public UObject { USceneComponent* get_component_by_class(UClass* uclass); UCameraComponent* get_camera_component(); - UActorComponent* add_component_by_class(UClass* uclass); + UActorComponent* add_component_by_class(UClass* uclass, bool deferred = false); + UActorComponent* add_component_by_class_ex(UClass* uclass, bool deferred = false); void finish_add_component(sdk::UObject* component); + void finish_add_component_ex(sdk::UObject* component); std::vector get_components_by_class(UClass* uclass); std::vector get_all_components(); @@ -34,6 +37,12 @@ class AActor : public UObject { void destroy_component(UActorComponent* component); void destroy_actor(); + USceneComponent* get_root_component(); + void set_root_component(USceneComponent* component); + + UObject* get_level(); + UWorld* get_world(); + protected: }; } \ No newline at end of file diff --git a/shared/sdk/UActorComponent.cpp b/shared/sdk/UActorComponent.cpp index 25cac9d6..7c8aab40 100644 --- a/shared/sdk/UActorComponent.cpp +++ b/shared/sdk/UActorComponent.cpp @@ -1,3 +1,10 @@ +#include + +#include +#include + +#include "EngineModule.hpp" + #include "UObjectArray.hpp" #include "UClass.hpp" #include "AActor.hpp" @@ -34,4 +41,58 @@ void UActorComponent::destroy_component() { owner->destroy_component(this); } } + +void UActorComponent::register_component_with_world(UWorld* world) { + using Fn = void(*)(UActorComponent*, UWorld*, void*, void*); + static auto fn = []() -> Fn { + SPDLOG_INFO("Finding UActorComponent::RegisterComponentWithWorld"); + + Fn result{}; + + const auto module = sdk::get_ue_module(L"Engine"); + const auto str = utility::scan_string(module, L"PC_InputComponent0"); + + if (!str) { + SPDLOG_ERROR("Failed to find UActorComponent::RegisterComponentWithWorld (no string)"); + return result; + } + + const auto ref = utility::scan_displacement_reference(module, *str); + + if (!ref) { + SPDLOG_ERROR("Failed to find UActorComponent::RegisterComponentWithWorld (no reference)"); + return result; + } + + const auto next_instr = *ref + 4; + + utility::exhaustive_decode((uint8_t*)next_instr, 300, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult { + if (result != nullptr) { + return utility::ExhaustionResult::BREAK; + } + + if (ctx.instrux.IsRipRelative && std::string_view{ctx.instrux.Mnemonic}.starts_with("CALL")) { + const auto called_fn = utility::resolve_displacement(ctx.addr); + + if (!called_fn) { + return utility::ExhaustionResult::STEP_OVER; + } + + if (utility::find_string_reference_in_path(*called_fn, L"ActorComponent")) { + result = (Fn)*called_fn; + SPDLOG_INFO("Found UActorComponent::RegisterComponentWithWorld at {:x}", *called_fn); + return utility::ExhaustionResult::BREAK; + } + + return utility::ExhaustionResult::STEP_OVER; + } + + return utility::ExhaustionResult::CONTINUE; + }); + + return result; + }(); + + fn(this, world, nullptr, nullptr); +} } \ No newline at end of file diff --git a/shared/sdk/UActorComponent.hpp b/shared/sdk/UActorComponent.hpp index 43cb5697..cb3a27e0 100644 --- a/shared/sdk/UActorComponent.hpp +++ b/shared/sdk/UActorComponent.hpp @@ -2,6 +2,7 @@ namespace sdk { class AActor; +class UWorld; class UActorComponent : public UObject { public: @@ -9,5 +10,6 @@ class UActorComponent : public UObject { AActor* get_owner(); void destroy_component(); + void register_component_with_world(UWorld* world); }; } \ No newline at end of file diff --git a/shared/sdk/UGameplayStatics.cpp b/shared/sdk/UGameplayStatics.cpp index cdb81c3a..6772a215 100644 --- a/shared/sdk/UGameplayStatics.cpp +++ b/shared/sdk/UGameplayStatics.cpp @@ -135,4 +135,25 @@ AActor* UGameplayStatics::finish_spawning_actor(AActor* actor, const glm::vec3& this->process_event(func, ¶ms_ue4); return params_ue4.result; } + +UObject* UGameplayStatics::spawn_object(UClass* uclass, UObject* outer) { + static const auto func = static_class()->find_function(L"SpawnObject"); + + if (func == nullptr) { + return nullptr; + } + + struct { + UClass* uclass{}; + UObject* outer{}; + UObject* result{}; + } params{}; + + params.uclass = uclass; + params.outer = outer; + + this->process_event(func, ¶ms); + + return params.result; +} } \ No newline at end of file diff --git a/shared/sdk/UGameplayStatics.hpp b/shared/sdk/UGameplayStatics.hpp index 13e320b4..907fbe70 100644 --- a/shared/sdk/UGameplayStatics.hpp +++ b/shared/sdk/UGameplayStatics.hpp @@ -28,6 +28,8 @@ class UGameplayStatics : public UObject { return nullptr; } + UObject* spawn_object(UClass* uclass, UObject* outer); + protected: }; } \ No newline at end of file diff --git a/shared/sdk/USceneComponent.cpp b/shared/sdk/USceneComponent.cpp index 07b16974..29e645af 100644 --- a/shared/sdk/USceneComponent.cpp +++ b/shared/sdk/USceneComponent.cpp @@ -230,4 +230,29 @@ glm::vec3 USceneComponent::get_world_rotation() { return *(glm::vec<3, double>*)params.data(); } + +bool USceneComponent::attach_to(USceneComponent* parent, const std::wstring& socket_name, uint8_t attach_type, bool weld) { + static const auto func = static_class()->find_function(L"K2_AttachTo"); + + if (func == nullptr) { + return false; + } + + struct { + USceneComponent* parent{}; + FName socket_name{L"None"}; + uint8_t attach_type{}; + bool weld{}; + bool result{}; + } params{}; + + params.parent = parent; + params.socket_name = FName{socket_name}; + params.attach_type = attach_type; + params.weld = weld; + + this->process_event(func, ¶ms); + + return params.result; +} } \ No newline at end of file diff --git a/shared/sdk/USceneComponent.hpp b/shared/sdk/USceneComponent.hpp index 5e32d048..61b266c6 100644 --- a/shared/sdk/USceneComponent.hpp +++ b/shared/sdk/USceneComponent.hpp @@ -19,5 +19,7 @@ class USceneComponent : public UActorComponent { glm::vec3 get_world_location(); glm::vec3 get_world_rotation(); + + bool attach_to(USceneComponent* parent, const std::wstring& socket_name = L"None", uint8_t attach_type = 0, bool weld = false); }; } \ No newline at end of file