Files
ReshadePluginsCore/libs/UEngine/UETools.cpp

376 lines
16 KiB
C++

#include "UETools.hpp"
#include "Engine_classes.hpp"
std::shared_ptr<spdlog::logger> g_logger;
SDK::APawn* GetPawnFromWorld(SDK::UWorld* world) {
if (!world) world = SDK::UWorld::GetWorld();
if (!world) return nullptr;
SDK::UGameInstance* gameInstance = world->OwningGameInstance;
if (!gameInstance || gameInstance->LocalPlayers.Num() == 0) return nullptr;
SDK::ULocalPlayer* localPlayer = gameInstance->LocalPlayers[0];
if (!localPlayer) return nullptr;
SDK::APlayerController* pc = localPlayer->PlayerController;
if (!pc) return nullptr;
SDK::APawn* pawn = pc->AcknowledgedPawn;
return pawn;
}
void ReactivateDevConsole(std::shared_ptr<spdlog::logger> logger) {
g_logger = logger;
std::thread([logger]() {
auto start = std::chrono::high_resolution_clock::now(); // Measure the time to renable console
SDK::UEngine* Engine = nullptr;
for (int i = 0; i < 100; ++i) { // gives 10 seconds to find UE Engine
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Engine = SDK::UEngine::GetEngine();
if (Engine && Engine->ConsoleClass && Engine->GameViewport)
break;
}
if (!Engine || !Engine->ConsoleClass || !Engine->GameViewport) {
logger->error("Console could not be found in engine.");
return;
}
logger->info("Console found in engine");
/* Creates a new UObject of class-type specified by Engine->ConsoleClass */
SDK::UObject* NewObject = SDK::UGameplayStatics::SpawnObject(Engine->ConsoleClass, Engine->GameViewport);
if (NewObject) {
logger->info("Successfully spawned console object");
// Set the console viewport so that it will be displayed
Engine->GameViewport->ViewportConsole = static_cast<SDK::UConsole*>(NewObject);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
// Set the all the console shortkey to F2
for (int i = 0; i < SDK::UInputSettings::GetDefaultObj()->ConsoleKeys.Num(); i++)
{
SDK::UInputSettings::GetDefaultObj()->ConsoleKeys[i].KeyName = SDK::UKismetStringLibrary::Conv_StringToName(L"F2");
}
logger->info("Console fully reactivated in {:.3f}s and bound to key F2", elapsed.count());
logger->info("------------------ User inputs ------------------");
g_Console_Enabled.store(true, std::memory_order_release);
return;
}
else {
logger->error("Could not spawn console object");
return;
}
}).detach();
}
// --- HUD & UI methods ---
void ApplyOffsetsSmart_Internal( SDK::UWidget* Widget, float Left, float Right, int Depth = 0, int MaxDepth = 1) {
if (!Widget) return;
// Apply if CanvasSlot
if (Widget->Slot && Widget->Slot->IsA(SDK::UCanvasPanelSlot::StaticClass())) {
if (Depth <= MaxDepth) {
auto* Slot = static_cast<SDK::UCanvasPanelSlot*>(Widget->Slot);
SDK::FMargin Offsets = Slot->GetOffsets();
SDK::FAnchors Anchors = Slot->GetAnchors();
float MinX = Anchors.Minimum.X;
float MaxX = Anchors.Maximum.X;
#ifdef MY_VERBOSE_LOGS
if (g_logger) g_logger->debug("CanvasPanelSlot: {} {} - Slot: {} {} - Offets: {} {} {} {} - Anchors: {} {} {} {}",
Widget->GetName(), Widget->Class->GetName(),
Slot->GetName(), Slot->Class->GetName(),
Offsets.Left, Offsets.Top, Offsets.Right, Offsets.Bottom,
Anchors.Minimum.X, Anchors.Minimum.Y, Anchors.Maximum.X, Anchors.Maximum.Y);
#endif
if (MinX == 0.f && MaxX == 1.f) return; // Ignore stretch
if (MinX == 0.5f && MaxX == 0.5f) return; // Ignore centered
if (MinX == 0.f && MaxX == 0.f) // Modify only pure left
Offsets.Left = Left;
else if (MinX == 1.f && MaxX == 1.f) // Modify only pure right
Offsets.Left = -Right;
Slot->SetOffsets(Offsets);
}
}
// Stop to max depth
if (Depth >= MaxDepth) return;
// Browse the children
if (Widget->IsA(SDK::UPanelWidget::StaticClass())) {
auto* Panel = static_cast<SDK::UPanelWidget*>(Widget);
for (int i = 0; i < Panel->GetChildrenCount(); ++i)
ApplyOffsetsSmart_Internal(Panel->GetChildAt(i), Left, Right, Depth + 1, MaxDepth);
}
if (Widget->IsA(SDK::UUserWidget::StaticClass())) {
auto* UW = static_cast<SDK::UUserWidget*>(Widget);
if (UW->WidgetTree && UW->WidgetTree->RootWidget)
ApplyOffsetsSmart_Internal(UW->WidgetTree->RootWidget, Left, Right, Depth + 1, MaxDepth);
}
}
void ApplyOffsetsSmart(SDK::UWidget* Widget, float Left, float Right, int MaxDepth) {
#ifdef MY_VERBOSE_LOGS
if (g_logger) g_logger->debug("=========================================================");
if (g_logger) g_logger->debug("Widget offset applying: {} {}",
Widget->GetName(), Widget->Class->GetName());
if (g_logger) g_logger->debug("=========================================================");
#endif
ApplyOffsetsSmart_Internal(Widget, Left, Right, 0, MaxDepth);
}
static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, float Right,
const std::vector<SDK::UObject*>& ExcludeObjects, const std::vector<std::string>& ExcludeClass,
int CurrentDepth, int MaxDepth) {
if (!Widget || CurrentDepth > MaxDepth) return;
// Apply offsets according to Slot type
for (const auto& ExcludeObject : ExcludeObjects) {
if (ExcludeObject == Widget) return;
}
std::string widgetName;
std::string className;
widgetName = Widget->GetName();
if (Widget->Class) className = Widget->Class->GetName();
for (const auto& pattern : ExcludeClass) {
if (widgetName.find(pattern) != std::string::npos || className.find(pattern) != std::string::npos)
return;
}
if (Widget->Slot) {
if (Widget->Slot->IsA(SDK::UCanvasPanelSlot::StaticClass())) {
auto* Slot = static_cast<SDK::UCanvasPanelSlot*>(Widget->Slot);
SDK::FMargin Offsets = Slot->GetOffsets();
#ifdef MY_VERBOSE_LOGS
SDK::FAnchors anchors = Slot->GetAnchors();
if (g_logger) g_logger->debug("CanvasPanelSlot: {} {} - Slot: {} {} - Offets: {} {} {} {} - Anchors: {} {} {} {}", \
Widget->GetName(), Widget->Class->GetName(), \
Slot->GetName(), Slot->Class->GetName(), \
Offsets.Left, Offsets.Top, Offsets.Right, Offsets.Bottom, \
anchors.Minimum.X, anchors.Minimum.Y, anchors.Maximum.X, anchors.Maximum.Y);
#endif
Offsets.Left = Left;
Offsets.Right = Right;
Slot->SetOffsets(Offsets);
}
else if (Widget->Slot->IsA(SDK::UVerticalBoxSlot::StaticClass())) {
auto* Slot = static_cast<SDK::UVerticalBoxSlot*>(Widget->Slot);
SDK::FMargin Margin = Slot->Padding;
#ifdef MY_VERBOSE_LOGS
if (g_logger) g_logger->debug("VerticalBox Slot: {} {} - Slot: {} {} - Margins: {} {} {} {}", \
Widget->GetName(), Widget->Class->GetName(), \
Slot->GetName(), Slot->Class->GetName(), \
Margin.Left, Margin.Top, Margin.Right, Margin.Bottom);
#endif
Margin.Left = Left;
Margin.Right = Right;
Slot->SetPadding(Margin);
}
}
// Go deeper in children if we got a Panel
if (Widget->IsA(SDK::UPanelWidget::StaticClass()) && CurrentDepth < MaxDepth) {
auto* Panel = static_cast<SDK::UPanelWidget*>(Widget);
int childrenCount = Panel->GetChildrenCount();
for (int i = 0; i < childrenCount; ++i) {
ApplyOffsetsRecursive_Internal(Panel->GetChildAt(i), Left, Right, ExcludeObjects, ExcludeClass, CurrentDepth + 1, MaxDepth);
}
}
// Go deeper if the widget is an UserWidget
if (Widget->IsA(SDK::UUserWidget::StaticClass())) {
auto* ChildWidget = static_cast<SDK::UUserWidget*>(Widget);
if (ChildWidget->WidgetTree && ChildWidget->WidgetTree->RootWidget) {
ApplyOffsetsRecursive_Internal(ChildWidget->WidgetTree->RootWidget, Left, Right, ExcludeObjects, ExcludeClass, CurrentDepth + 1, MaxDepth);
}
}
}
void ApplyOffsetsRecursive(SDK::UWidget* Widget, float Left, float Right,
const std::vector<SDK::UObject*>& ExcludeObjects, const std::vector<std::string>& ExcludeClass, int MaxDepth) {
ApplyOffsetsRecursive_Internal(Widget, Left, Right, ExcludeObjects, ExcludeClass, 0, MaxDepth);
}
static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float left, float right, SDK::EHorizontalAlignment alignment,
const std::vector<std::string>& ExcludeNames, int CurrentDepth, int MaxDepth) {
if (!Widget || CurrentDepth > MaxDepth) return;
auto IsExcluded = [&](SDK::UWidget* widget) -> bool {
for (const auto& name : ExcludeNames) {
if (widget->GetName().contains(name))
return true;
}
return false;
};
auto AdjustOverlaySlot = [&](SDK::UOverlaySlot* Slot) {
if (!Slot || IsExcluded(Widget)) return;
SDK::FMargin Padding = Slot->Padding;
if (Slot->HorizontalAlignment == alignment) {
Padding.Left = left;
Padding.Right = right;
}
Slot->SetPadding(Padding);
};
if (Widget->Slot && Widget->Slot->IsA(SDK::UOverlaySlot::StaticClass()))
AdjustOverlaySlot((SDK::UOverlaySlot*)Widget->Slot);
if (Widget->IsA(SDK::UPanelWidget::StaticClass()) && CurrentDepth < MaxDepth) {
SDK::UPanelWidget* Panel = (SDK::UPanelWidget*)Widget;
int childrenCount = Panel->GetChildrenCount();
for (int i = 0; i < childrenCount; ++i) {
ApplyOverlayOffsetRecursive_Internal(Panel->GetChildAt(i), left, right, alignment, ExcludeNames, CurrentDepth + 1, MaxDepth);
}
}
}
void ApplyOverlayOffsetRecursive(SDK::UWidget* Widget, float left, float right, SDK::EHorizontalAlignment alignment,
const std::vector<std::string>& ExcludeNames, int MaxDepth) {
ApplyOverlayOffsetRecursive_Internal(Widget, left, right, alignment, ExcludeNames, 0, MaxDepth);
}
void FindAndApplyCanvasRecursive(SDK::UWidget* widget, float offset, int MaxDepth, int currentDepth) {
if (!widget || currentDepth > MaxDepth) return;
// Si c'est un CanvasPanel, on applique directement
if (widget->IsA(SDK::UCanvasPanel::StaticClass())) {
ApplyOffsetsRecursive(widget, offset, offset);
}
// Si c'est un panel, on descend dans les enfants
if (widget->IsA(SDK::UPanelWidget::StaticClass())) {
auto* panel = static_cast<SDK::UPanelWidget*>(widget);
int childrenCount = panel->GetChildrenCount();
for (int i = 0; i < childrenCount; ++i) {
FindAndApplyCanvasRecursive(panel->GetChildAt(i), offset, MaxDepth, currentDepth + 1);
}
}
}
void TrackWidgetConstruct(SDK::UUserWidget* widget) {
if (!widget || !widget->Class) return;
std::string className = widget->Class->GetName();
auto& info = g_WidgetTracker[className];
info.ClassName = className;
info.ConstructCount++;
if (info.ConstructCount == 1)
info.FirstSeen = std::chrono::steady_clock::now();
if (widget->IsInViewport())
info.WasInViewport = true;
if (widget->Outer && widget->Outer->Class)
info.OuterClass = widget->Outer->Class->GetName();
if (widget->WidgetTree && widget->WidgetTree->RootWidget) {
auto* root = widget->WidgetTree->RootWidget;
if (root && root->Class)
info.RootWidgetClass = root->Class->GetName();
}
}
void TrackWidgetDestruct(SDK::UUserWidget* widget) {
if (!widget || !widget->Class) return;
std::string className = widget->Class->GetName();
if (g_WidgetTracker.contains(className))
g_WidgetTracker[className].DestructCount++;
}
static std::string ClassifyWidget(const WidgetTrackInfo& info) {
bool persistent = info.ConstructCount > info.DestructCount;
if (info.WasInViewport && persistent && info.OuterClass.find("PlayerController") != std::string::npos)
return "HUD Candidate";
if (info.WasInViewport && (info.ClassName.find("Menu") != std::string::npos))
return "Menu UI";
if (info.WasInViewport && (info.ClassName.find("Settings") != std::string::npos))
return "Settings UI";
if (info.WasInViewport && (info.ClassName.find("Save") != std::string::npos ||
info.ClassName.find("Load") != std::string::npos))
return "Save/Load UI";
if (!persistent)
return "Temporary Widget";
return "Unclassified";
}
void DumpUIAnalysis(std::shared_ptr<spdlog::logger> logger) {
if (!logger) return;
logger->info("========== UI ANALYSIS ==========");
for (auto& [name, info] : g_WidgetTracker) {
std::string type = ClassifyWidget(info);
bool persistent = info.ConstructCount > info.DestructCount;
logger->info("Class: {}", name);
logger->info(" Type : {}", type);
logger->info(" Root Type : {}", info.RootWidgetClass.empty() ? "Unknown" : info.RootWidgetClass);
logger->info(" Construct : {}", info.ConstructCount);
logger->info(" Destruct : {}", info.DestructCount);
logger->info(" Persistent : {}", persistent ? "Yes" : "No");
logger->info(" InViewport : {}", info.WasInViewport ? "Yes" : "No");
logger->info(" Outer : {}", info.OuterClass.empty() ? "Unknown" : info.OuterClass);
logger->info(" ");
}
logger->info("=================================");
}
void ClearWidgetTracking() {
g_WidgetTracker.clear();
}
void DumpWidgetRecursive(SDK::UWidget* Widget, int Depth, int MaxDepth) {
if (!Widget || Depth > MaxDepth) return;
std::string indent(Depth * 2, ' ');
std::string className = Widget->Class ? Widget->Class->GetName() : "UnknownClass";
std::string widgetName = Widget->GetName();
if (g_logger)
g_logger->debug("{}Widget: {} [{}]", indent, widgetName, className);
// Slot info si disponible
if (Widget->Slot) {
std::string slotClass = Widget->Slot->Class ? Widget->Slot->Class->GetName() : "UnknownSlot";
if (g_logger)
g_logger->debug("{} Slot class: {}", indent, slotClass);
}
// Descendre dans les enfants si c'est un panel et qu'on est encore sous MaxDepth
if (Depth < MaxDepth) {
if (Widget->IsA(SDK::UPanelWidget::StaticClass())) {
auto* panel = static_cast<SDK::UPanelWidget*>(Widget);
int count = panel->GetChildrenCount();
for (int i = 0; i < count; ++i) {
DumpWidgetRecursive(panel->GetChildAt(i), Depth + 1, MaxDepth);
}
}
if (Widget->IsA(SDK::UUserWidget::StaticClass())) {
auto* userWidget = static_cast<SDK::UUserWidget*>(Widget);
if (userWidget->WidgetTree && userWidget->WidgetTree->RootWidget) {
DumpWidgetRecursive(userWidget->WidgetTree->RootWidget, Depth + 1, MaxDepth);
}
}
}
}