#Instanced structs in DevSettings
1 messages · Page 1 of 1 (latest)
I spent last night debugging. It is indeed a bug in engine, at least in 5.5.
diving in deeper from saving devsettings to ini in reverse order:
SSettingsEditor::NotifyPostChangeis what triggers saving, butGetNumObjectsBeingEditedreturns 0 forFInstancedStructand nothing happens. Editor expects it to be UDevSettings but TopLevelObjects is emptySSettingsEditor::NotifyPostChangeinvoked fromFPropertyNode::NotifyPostChangeFPropertyNode::NotifyPostChangeis invoked fromFPropertyValueImpl::ImportTextwhereFPropertyValueImpl::GetObjectsToModifycollects list of modified objectsFPropertyValueImpl::GetObjectsToModifycallsFPropertyValueImpl::EnumerateObjectsToModifyFPropertyValueImpl::EnumerateObjectsToModifyhas a weird logic - instanced struct is being treated like a standalone struct and not member of uobject
In this case and FObjectBaseAddress.Object not being populated (has to be UDevSettings instance)
const bool bIsStruct = FComplexPropertyNode::EPT_StandaloneStructure == ComplexNode->GetPropertyType();
UObject* Object = nullptr;
if (bIsStruct)
{ // this part is used for FInstancedStruct, ComplexNode=FStructurePropertyNode with FInstancedStructProvider
// it's ParentNode FObjectPropertyNode containing UDevSettings (being accessed by FInstancedStructProvider)
StructAddress = ComplexNode->GetMemoryOfInstance(Index);
}
else
{ // for regular struct member ComplexNode=FObjectPropertyNode, Object is UDevSettings
Object = ComplexNode->GetInstanceAsUObject(Index).Get();
StructAddress = InPropertyNode->GetStartAddressFromObject(Object);
}
and then FPropertyValueImpl::ImportText uses that data to determine modified object
UObject* CurObject = Cur.Object; // object being modified (DevSettings)
const bool bIsStruct = !Cur.Object; // why not check for EPT_StandaloneStructure ???????
TopLevelObjects.Add(CurObject); // here adding top level object that is checked in SSettingsEditor later
// later event constructed and populated without any objects
FPropertyChangedEvent ChangeEvent(..., MakeArrayView(TopLevelObjects));
// there is no point checking FPropertyNode::NotifyPostChange further as ChangeEvent does not change and lacking data about base object
fixing manually FObjectBaseAddress.Object and how bIsStruct determined later seems to fix resulting the value in TopLevelObjects for both regular and container use
i'd say a new virtual has to be added to ComplexNode and forwarded to StructProvider to test if it is a standalone or a indirect member
workaround - just save it manually with this universal fixer:
#if WITH_EDITOR
void USampleDeveloperSettings::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
{
Super::PostEditChangeChainProperty(PropertyChangedEvent);
// perform forced save only on FInstancedStruct property change
// instead of name can check for being instanced struct type
if (PropertyChangedEvent.PropertyChain.GetHead()->GetValue()->GetName() == GET_MEMBER_NAME_CHECKED(ThisClass, Sample))
{ // it is safe to directly call Save as due to bug SSettingsEditor::NotifyPostChange wont work due to PropertyChangedEvent.TopLevelObject being empty
auto& SettingsModule = FModuleManager::GetModuleChecked<ISettingsModule>("Settings");
auto Section = SettingsModule.GetContainer(GetContainerName())->GetCategory(GetCategoryName())->GetSection(GetSectionName());
Section->Save();
}
}
#endif
PostEditChangeChainProperty is invoked on DevSettings instance prior to NotifyPostChange, but SSettingsEdior will never handle it because event lacks of object
I've tested workaround fix for regular and arrays - worked fine, well. it resaves entire section when IS changes