// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Concepts/EqualityComparable.h" #include "Containers/StringFwd.h" #include "Containers/StringView.h" #include "Delegates/Delegate.h" #include "Dom/JsonObject.h" #include "HAL/Platform.h" #include "JsonConfig.h" #include "Misc/AssertionMacros.h" #include "Templates/IsPointer.h" #include "Templates/Models.h" #include "Templates/SharedPointer.h" #include "Templates/UnrealTemplate.h" #include "Templates/UnrealTypeTraits.h" class FArrayProperty; class FJsonObject; class FJsonValue; class FMapProperty; class FProperty; class FSetProperty; class FString; class UClass; class UObject; class UStruct; class EDITORCONFIG_API FEditorConfig { public: enum class EPropertyFilter { All, MetadataOnly }; FEditorConfig(); void SetParent(TSharedPtr InConfig); bool LoadFromString(FStringView Content); bool LoadFromFile(FStringView FilePath); bool SaveToString(FString& OutResult) const; bool IsValid() const { return JsonConfig.IsValid() && JsonConfig->IsValid(); } // UStruct & UObject template bool TryGetStruct(FStringView Key, T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; template bool TryGetUObject(FStringView Key, T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; template bool TryGetRootStruct(T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; template bool TryGetRootUObject(T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; template bool TryGetRootUObject(T* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; bool TryGetRootStruct(const UStruct* Class, void* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; bool TryGetRootUObject(const UClass* Class, UObject* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const; template void SetStruct(FStringView Key, const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); template void SetUObject(FStringView Key, const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); template void SetRootStruct(const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); template void SetRootUObject(const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); void SetRootStruct(const UStruct* Class, const void* Instance, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); void SetRootUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter = EPropertyFilter::MetadataOnly); bool HasOverride(FStringView Key) const; void OnSaved(); DECLARE_EVENT_OneParam(FEditorConfig, FOnEditorConfigDirtied, const FEditorConfig&); FOnEditorConfigDirtied& OnEditorConfigDirtied() { return EditorConfigDirtiedEvent; } private: friend class UEditorConfigSubsystem; // for access to LoadFromFile and SaveToFile static void ReadUObject(const TSharedPtr& JsonObject, const UClass* Class, UObject* Instance, EPropertyFilter Filter); static void ReadStruct(const TSharedPtr& JsonObject, const UStruct* Struct, void* Instance, UObject* Owner, EPropertyFilter Filter); static void ReadValue(const TSharedPtr& JsonValue, const FProperty* Property, void* DataPtr, UObject* Owner); static TSharedPtr WriteStruct(const UStruct* Struct, const void* Instance, const void* Defaults, EPropertyFilter Filter); static TSharedPtr WriteUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter); static TSharedPtr WriteArray(const FArrayProperty* ArrayProperty, const void* DataPtr); static TSharedPtr WriteSet(const FSetProperty* Property, const void* DataPtr); static TSharedPtr WriteMap(const FMapProperty* Property, const void* DataPtr); static TSharedPtr WriteValue(const FProperty* Property, const void* DataPtr, const void* Defaults); bool SaveToFile(FStringView FilePath) const; void SetDirty(); private: TSharedPtr JsonConfig; TSharedPtr ParentConfig; FOnEditorConfigDirtied EditorConfigDirtiedEvent; bool Dirty { false }; }; template bool FEditorConfig::TryGetStruct(FStringView Key, T& OutValue, EPropertyFilter Filter) const { if (!IsValid()) { return false; } TSharedPtr StructData; UE::FJsonPath Path(Key); if (!JsonConfig->TryGetJsonObject(Path, StructData)) { return false; } if (!StructData.IsValid()) { return false; } const UStruct* Struct = T::StaticStruct(); ReadStruct(StructData, Struct, &OutValue, nullptr, Filter); return true; } template bool FEditorConfig::TryGetUObject(FStringView Key, T& OutValue, EPropertyFilter Filter) const { static_assert(TIsDerivedFrom::Value, "Type is not derived from UObject."); if (!IsValid()) { return false; } TSharedPtr UObjectData; UE::FJsonPath Path(Key); if (!JsonConfig->TryGetJsonObject(Path, UObjectData)) { return false; } if (!UObjectData.IsValid()) { return false; } const UClass* Class = T::StaticClass(); ReadUObject(UObjectData, Class, &OutValue, Filter); return true; } template bool FEditorConfig::TryGetRootStruct(T& OutValue, EPropertyFilter Filter) const { return TryGetRootStruct(T::StaticStruct(), &OutValue, Filter); } template bool FEditorConfig::TryGetRootUObject(T& OutValue, EPropertyFilter Filter) const { static_assert(TIsDerivedFrom::Value, "Type is not derived from UObject."); return TryGetRootUObject(T::StaticClass(), &OutValue, Filter); } template bool FEditorConfig::TryGetRootUObject(T* OutValue, EPropertyFilter Filter) const { static_assert(TIsDerivedFrom::Value, "Type is not derived from UObject."); checkf(OutValue != nullptr, TEXT("Output value was null.")); return TryGetRootUObject(T::StaticClass(), OutValue, Filter); } template void FEditorConfig::SetStruct(FStringView Key, const T& InValue, EPropertyFilter Filter) { if (!IsValid()) { return; } TSharedPtr JsonObject = WriteStruct(T::StaticStruct(), &InValue, nullptr, Filter); JsonConfig->SetJsonObject(UE::FJsonPath(Key), JsonObject); SetDirty(); } template void FEditorConfig::SetUObject(FStringView Key, const T& InValue, EPropertyFilter Filter) { static_assert(TIsDerivedFrom::Value, "Type is not derived from UObject."); if (!IsValid()) { return; } TSharedPtr JsonObject = WriteUObject(T::StaticClass(), &InValue); JsonConfig->SetJsonObject(UE::FJsonPath(Key), JsonObject); SetDirty(); } template void FEditorConfig::SetRootStruct(const T& InValue, EPropertyFilter Filter) { SetRootStruct(T::StaticStruct(), &InValue, Filter); } template void FEditorConfig::SetRootUObject(const T& InValue, EPropertyFilter Filter) { if constexpr (TIsPointer::Value) { static_assert(TIsDerivedFrom::Type, UObject>::Value, "Type is not derived from UObject."); checkf(InValue != nullptr, TEXT("Object value was null.")); SetRootUObject(TRemovePointer::Type::StaticClass(), InValue, Filter); } else { static_assert(TIsDerivedFrom::Type, UObject>::Value, "Type is not derived from UObject."); SetRootUObject(T::StaticClass(), &InValue, Filter); } }