Skip to content

Commit 33d676b

Browse files
authored
Merge pull request #1080 from UE4SS-RE/live-view-struct-too-large
fix(LiveView): Don't render values that are too large
2 parents 9ae6909 + 97ee0c1 commit 33d676b

File tree

3 files changed

+136
-9
lines changed

3 files changed

+136
-9
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <cstdint>
5+
6+
#include <String/StringType.hpp>
7+
8+
namespace RC::GUI::Filter
9+
{
10+
class MaxValueSize
11+
{
12+
public:
13+
static inline StringType s_debug_name{STR("MaxValueSize")};
14+
static inline int32_t s_value{500};
15+
static inline std::string s_value_buffer{fmt::format("{}", s_value)};
16+
};
17+
} // namespace RC::GUI::Filter

UE4SS/src/GUI/LiveView.cpp

Lines changed: 117 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <GUI/LiveView/Filter/InstancesOnly.hpp>
2424
#include <GUI/LiveView/Filter/NonInstancesOnly.hpp>
2525
#include <GUI/LiveView/Filter/SearchFilter.hpp>
26+
#include <GUI/LiveView/Filter/MaxValueSize.hpp>
2627
#include <GUI/UFunctionCallerWidget.hpp>
2728
#include <Helpers/String.hpp>
2829
#include <JSON/JSON.hpp>
@@ -35,6 +36,7 @@
3536
#include <Unreal/Property/FObjectProperty.hpp>
3637
#include <Unreal/Property/FEnumProperty.hpp>
3738
#include <Unreal/Property/NumericPropertyTypes.hpp>
39+
#include <Unreal/Property/FMapProperty.hpp>
3840
#include <Unreal/UClass.hpp>
3941
#include <Unreal/UEnum.hpp>
4042
#include <Unreal/UFunction.hpp>
@@ -101,6 +103,7 @@ namespace RC::GUI
101103
void* container = nullptr;
102104
UObject* obj = nullptr;
103105
LiveView::ContainerType container_type = LiveView::ContainerType::Object;
106+
bool editable = true;
104107
};
105108
static DeferredPropertyEditPopup s_deferred_property_edit_popup{};
106109

@@ -336,6 +339,14 @@ namespace RC::GUI
336339
filter_data.new_bool(STR("Enabled"), is_enabled);
337340
}
338341

342+
static auto add_int32_filter_to_json(JSON::Array& json_filters, const StringType& filter_name, int32_t value) -> void
343+
{
344+
auto& json_object = json_filters.new_object();
345+
json_object.new_string(STR("FilterName"), filter_name);
346+
auto& filter_data = json_object.new_object(STR("FilterData"));
347+
filter_data.new_number(STR("Value"), value);
348+
}
349+
339350
template <typename ContainerType>
340351
static auto add_array_filter_to_json(JSON::Array& json_filters, const StringType& filter_name, const ContainerType& container, const StringType& array_name)
341352
-> void
@@ -391,6 +402,9 @@ namespace RC::GUI
391402
add_array_filter_to_json(json_filters, Filter::HasPropertyType::s_debug_name, Filter::HasPropertyType::list_property_types, STR("PropertyTypes"));
392403
add_array_filter_to_json(json_filters, Filter::FunctionParamFlags::s_debug_name, Filter::FunctionParamFlags::s_checkboxes, STR("FunctionParamFlags"));
393404
}
405+
{
406+
add_int32_filter_to_json(json_filters, Filter::MaxValueSize::s_debug_name, Filter::MaxValueSize::s_value);
407+
}
394408

395409
auto json_file = File::open(StringType{UE4SSProgram::get_program().get_working_directory()} + fmt::format(STR("\\liveview\\filters.meta.json")),
396410
File::OpenFor::Writing,
@@ -530,6 +544,16 @@ namespace RC::GUI
530544
return LoopAction::Continue;
531545
});
532546
}
547+
else if (filter_name == Filter::MaxValueSize::s_debug_name)
548+
{
549+
auto number = filter_data.get<JSON::Number>(STR("Value")).get<int64_t>();
550+
if (number > std::numeric_limits<int32_t>::max())
551+
{
552+
number = std::numeric_limits<int32_t>::max();
553+
}
554+
Filter::MaxValueSize::s_value = static_cast<int32_t>(number);
555+
Filter::MaxValueSize::s_value_buffer = fmt::format("{}", Filter::MaxValueSize::s_value);
556+
}
533557

534558
return LoopAction::Continue;
535559
});
@@ -2198,7 +2222,38 @@ namespace RC::GUI
21982222
FString property_text{};
21992223
auto property_name = to_string(property->GetName());
22002224
auto container_ptr = property->ContainerPtrToValuePtr<void*>(container);
2201-
property->ExportTextItem(property_text, container_ptr, container_ptr, static_cast<UObject*>(container), NULL);
2225+
auto as_struct_property = CastField<FStructProperty>(property);
2226+
static constexpr auto s_error_too_large = STR("Too large to display on one line! Click to view individual members.");
2227+
bool editable = true;
2228+
if (auto as_map_property = CastField<FMapProperty>(property))
2229+
{
2230+
auto map = std::bit_cast<FScriptMap*>(container_ptr);
2231+
if (auto value_as_struct_property = CastField<FStructProperty>(as_map_property->GetValueProp());
2232+
value_as_struct_property && value_as_struct_property->GetStruct()->GetStructureSize() * map->Num() > Filter::MaxValueSize::s_value)
2233+
{
2234+
editable = false;
2235+
property_text = FString{s_error_too_large};
2236+
}
2237+
else
2238+
{
2239+
property->ExportTextItem(property_text, container_ptr, container_ptr, static_cast<UObject*>(container), NULL);
2240+
}
2241+
}
2242+
else if (auto as_array_property = CastField<FArrayProperty>(property);
2243+
as_array_property && as_array_property->GetSize() * std::bit_cast<FScriptArray*>(container_ptr)->Num() > Filter::MaxValueSize::s_value)
2244+
{
2245+
editable = false;
2246+
property_text = FString{s_error_too_large};
2247+
}
2248+
else if (as_struct_property && as_struct_property->GetStruct()->GetStructureSize() > Filter::MaxValueSize::s_value)
2249+
{
2250+
editable = false;
2251+
property_text = FString{s_error_too_large};
2252+
}
2253+
else
2254+
{
2255+
property->ExportTextItem(property_text, container_ptr, container_ptr, static_cast<UObject*>(container), NULL);
2256+
}
22022257

22032258
bool open_edit_value_popup{};
22042259

@@ -2215,7 +2270,9 @@ namespace RC::GUI
22152270
}
22162271
if (ImGui::MenuItem("Copy value"))
22172272
{
2218-
ImGui::SetClipboardText(to_string(*property_text).c_str());
2273+
FString snapshotted_value{};
2274+
property->ExportTextItem(snapshotted_value, container_ptr, container_ptr, static_cast<UObject*>(container), NULL);
2275+
ImGui::SetClipboardText(to_string(*snapshotted_value).c_str());
22192276
}
22202277
if (container_type == ContainerType::Object || container_type == ContainerType::Struct)
22212278
{
@@ -2286,20 +2343,16 @@ namespace RC::GUI
22862343
container_type == ContainerType::Array ? fmt::format("").c_str() : fmt::format(" (0x{:X})", property_offset).c_str(),
22872344
property_name.c_str());
22882345
}
2289-
if (auto struct_property = CastField<FStructProperty>(property); struct_property && struct_property->GetStruct()->GetFirstProperty())
2346+
if (as_struct_property && as_struct_property->GetStruct()->GetFirstProperty())
22902347
{
22912348
ImGui::SameLine();
22922349
auto tree_node_id = fmt::format("{}{}", static_cast<void*>(container_ptr), property_name);
22932350
if (ImGui_TreeNodeEx(fmt::format("{}", to_string(*property_text)).c_str(), tree_node_id.c_str(), ImGuiTreeNodeFlags_NoAutoOpenOnLog))
22942351
{
22952352
render_property_value_context_menu(tree_node_id);
22962353

2297-
for (FProperty* inner_property : struct_property->GetStruct()->ForEachProperty())
2354+
for (FProperty* inner_property : as_struct_property->GetStruct()->ForEachProperty())
22982355
{
2299-
FString struct_prop_text_item{};
2300-
auto struct_prop_container_ptr = inner_property->ContainerPtrToValuePtr<void*>(container_ptr);
2301-
inner_property->ExportTextItem(struct_prop_text_item, struct_prop_container_ptr, struct_prop_container_ptr, nullptr, NULL);
2302-
23032356
ImGui::Indent();
23042357
FProperty* last_struct_prop{};
23052358
next_item_to_render = render_property_value(inner_property,
@@ -2412,10 +2465,25 @@ namespace RC::GUI
24122465

24132466
if (open_edit_value_popup)
24142467
{
2468+
// Re-snapshot the value, because it may not be set due to it being too large of a type to export quickly.
2469+
// Why is this an empty string ? Wtf ?
2470+
// It seems it's not empty, but imgui isn't rendering the text properly for some reason, it's rendered as spaces.
2471+
// When you copy the text, it does copy the actual text, not spaces.
2472+
//FString snapshotted_property_text{};
2473+
//property->ExportTextItem(snapshotted_property_text, container_ptr, container_ptr, static_cast<UObject*>(container), NULL);
24152474
// Defer the popup opening - store all necessary context
24162475
s_deferred_property_edit_popup.pending = true;
24172476
s_deferred_property_edit_popup.modal_name = edit_property_value_modal_name;
2418-
s_deferred_property_edit_popup.initial_value = to_string(*property_text);
2477+
if (!editable)
2478+
{
2479+
s_deferred_property_edit_popup.editable = false;
2480+
s_deferred_property_edit_popup.initial_value = "Too large to edit!";
2481+
}
2482+
else
2483+
{
2484+
s_deferred_property_edit_popup.editable = true;
2485+
s_deferred_property_edit_popup.initial_value = to_string(*property_text);
2486+
}
24192487
s_deferred_property_edit_popup.property = property;
24202488
s_deferred_property_edit_popup.container = container;
24212489
s_deferred_property_edit_popup.obj = obj;
@@ -3397,6 +3465,10 @@ namespace RC::GUI
33973465
ImGui::Text("The game could crash if the new value is invalid.");
33983466
ImGui::Text("The game can override the new value immediately.");
33993467
ImGui::PushItemWidth(-1.0f);
3468+
if (!s_deferred_property_edit_popup.editable)
3469+
{
3470+
ImGui::BeginDisabled();
3471+
}
34003472
ImGui::InputText("##CurrentPropertyValue", &m_current_property_value_buffer);
34013473
if (ImGui::Button("Apply"))
34023474
{
@@ -3417,6 +3489,10 @@ namespace RC::GUI
34173489
s_deferred_property_edit_popup.property = nullptr; // Clear the deferred state
34183490
}
34193491
}
3492+
if (!s_deferred_property_edit_popup.editable)
3493+
{
3494+
ImGui::EndDisabled();
3495+
}
34203496

34213497
if (ImGui::BeginPopupModal("UnableToSetNewPropertyValueError",
34223498
&m_modal_edit_property_value_error_unable_to_edit,
@@ -3435,6 +3511,7 @@ namespace RC::GUI
34353511
if (!m_modal_edit_property_value_is_open)
34363512
{
34373513
s_deferred_property_edit_popup.property = nullptr;
3514+
s_deferred_property_edit_popup.editable = true;
34383515
}
34393516

34403517
// Handle deferred enum edit popup
@@ -3824,6 +3901,37 @@ namespace RC::GUI
38243901
}
38253902
}
38263903
}
3904+
// Row 8
3905+
ImGui::TableNextRow();
3906+
ImGui::TableNextColumn();
3907+
ImGui::Text("Maximum Value Size");
3908+
ImGui::TableNextColumn();
3909+
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
3910+
if (ImGui::InputText("##MaxValueSize", &Filter::MaxValueSize::s_value_buffer))
3911+
{
3912+
int32_t new_value{-1};
3913+
try
3914+
{
3915+
new_value = std::stoi(Filter::MaxValueSize::s_value_buffer);
3916+
}
3917+
catch (...)
3918+
{
3919+
if (Filter::MaxValueSize::s_value > 0)
3920+
{
3921+
new_value = std::numeric_limits<int32_t>::max();
3922+
}
3923+
else
3924+
{
3925+
new_value = 0;
3926+
}
3927+
}
3928+
if (new_value < 0)
3929+
{
3930+
new_value = Filter::MaxValueSize::s_value;
3931+
}
3932+
Filter::MaxValueSize::s_value = new_value;
3933+
Filter::MaxValueSize::s_value_buffer = fmt::format("{}", Filter::MaxValueSize::s_value);
3934+
}
38273935

38283936
ImGui::TableNextRow();
38293937
ImGui::TableNextColumn();

assets/Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ The search filter `ExcludeClassName` can now be found in the `IncludeClassNames`
264264

265265
The following search filters now allow multiple values, with each value separated by a comma: `IncludeClassNames`, `ExcludeClassNames`, `HasProperty`, `HasPropertyType`. ([UE4SS #472](https://github.com/UE4SS-RE/RE-UE4SS/pull/472)) - Buckminsterfullerene
266266

267+
Large structs that cause FPS problems no longer render, but you can still click on the struct to render each individual property of the struct. ([UE4SS #1080](https://github.com/UE4SS-RE/RE-UE4SS/pull/1080))
268+
267269
### UHT Dumper
268270

269271
### Lua API

0 commit comments

Comments
 (0)