#include "UETools.hpp" #include "Engine_classes.hpp" 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 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(NewObject); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration 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 --- static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, float Right, int CurrentDepth, int MaxDepth) { if (!Widget || CurrentDepth > MaxDepth) return; // Apply offsets according to Slot type if (Widget->Slot) { if (Widget->Slot->IsA(SDK::UCanvasPanelSlot::StaticClass())) { auto* Slot = static_cast(Widget->Slot); SDK::FMargin Offsets = Slot->GetOffsets(); Offsets.Left = Left; Offsets.Right = Right; Slot->SetOffsets(Offsets); } else if (Widget->Slot->IsA(SDK::UVerticalBoxSlot::StaticClass())) { auto* Slot = static_cast(Widget->Slot); SDK::FMargin Margin = Slot->Padding; 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(Widget); int childrenCount = Panel->GetChildrenCount(); for (int i = 0; i < childrenCount; ++i) { ApplyOffsetsRecursive_Internal(Panel->GetChildAt(i), Left, Right, CurrentDepth + 1, MaxDepth); } } // Go deeper if the widget is an UserWidget if (Widget->IsA(SDK::UUserWidget::StaticClass())) { auto* ChildWidget = static_cast(Widget); if (ChildWidget->WidgetTree && ChildWidget->WidgetTree->RootWidget) { ApplyOffsetsRecursive_Internal(ChildWidget->WidgetTree->RootWidget, Left, Right, CurrentDepth + 1, MaxDepth); } } } void ApplyOffsetsRecursive(SDK::UWidget* Widget, float Left, float Right, int MaxDepth) { ApplyOffsetsRecursive_Internal(Widget, Left, Right, 0, MaxDepth); } static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float Offset, SDK::EHorizontalAlignment alignment, const std::vector& 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 = Offset; Padding.Right = Offset; } 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), Offset, alignment, ExcludeNames, CurrentDepth + 1, MaxDepth); } } } void ApplyOverlayOffsetRecursive(SDK::UWidget* Widget, float Offset, SDK::EHorizontalAlignment alignment, const std::vector& ExcludeNames, int MaxDepth) { ApplyOverlayOffsetRecursive_Internal(Widget, Offset, 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(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 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(); }