Skip to content

Commit

Permalink
SDK: Fix component creation on < 4.27 (RegisterComponent)
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Oct 27, 2023
1 parent 4103c2b commit 357f3f3
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 8 deletions.
134 changes: 127 additions & 7 deletions shared/sdk/AActor.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
#include <vector>
#include <utility/String.hpp>

#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"

Expand Down Expand Up @@ -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<uint8_t> params{};
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -212,20 +242,76 @@ 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));
} else {
FTransformUE5 transform{};
params.insert(params.end(), (uint8_t*)&transform, (uint8_t*)&transform + sizeof(FTransformUE5));
}

/*std::vector<uint8_t> 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<UActorComponent*> AActor::get_components_by_class(UClass* uclass) {
static const auto func = AActor::static_class()->find_function(L"K2_GetComponentsByClass");

Expand Down Expand Up @@ -293,4 +379,38 @@ void AActor::destroy_actor() {

this->process_event(func, &params);
}

USceneComponent* AActor::get_root_component() {
return get_property<USceneComponent*>(L"RootComponent");
}

void AActor::set_root_component(USceneComponent* component) {
get_property<USceneComponent*>(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, &params);

return params.return_value;
}

UWorld* AActor::get_world() {
auto level = get_level();

if (level == nullptr) {
return nullptr;
}

return level->get_property<UWorld*>(L"OwningWorld");
}
}
11 changes: 10 additions & 1 deletion shared/sdk/AActor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class UCameraComponent;
class USceneComponent;
class UActorComponent;
class AActor;
class UWorld;

class AActor : public UObject {
public:
Expand All @@ -25,15 +26,23 @@ 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<UActorComponent*> get_components_by_class(UClass* uclass);
std::vector<UActorComponent*> get_all_components();

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:
};
}
61 changes: 61 additions & 0 deletions shared/sdk/UActorComponent.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#include <spdlog/spdlog.h>

#include <utility/Scan.hpp>
#include <utility/Module.hpp>

#include "EngineModule.hpp"

#include "UObjectArray.hpp"
#include "UClass.hpp"
#include "AActor.hpp"
Expand Down Expand Up @@ -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);
}
}
2 changes: 2 additions & 0 deletions shared/sdk/UActorComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

namespace sdk {
class AActor;
class UWorld;

class UActorComponent : public UObject {
public:
static UClass* static_class();

AActor* get_owner();
void destroy_component();
void register_component_with_world(UWorld* world);
};
}
21 changes: 21 additions & 0 deletions shared/sdk/UGameplayStatics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,25 @@ AActor* UGameplayStatics::finish_spawning_actor(AActor* actor, const glm::vec3&
this->process_event(func, &params_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, &params);

return params.result;
}
}
2 changes: 2 additions & 0 deletions shared/sdk/UGameplayStatics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class UGameplayStatics : public UObject {
return nullptr;
}

UObject* spawn_object(UClass* uclass, UObject* outer);

protected:
};
}
25 changes: 25 additions & 0 deletions shared/sdk/USceneComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, &params);

return params.result;
}
}
2 changes: 2 additions & 0 deletions shared/sdk/USceneComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}

0 comments on commit 357f3f3

Please sign in to comment.