diff --git a/Mods/ExampleMod/Content/ExampleModConfiguration.uasset b/Mods/ExampleMod/Content/ExampleModConfiguration.uasset index 2b2337bb85..3bd83ea9df 100644 Binary files a/Mods/ExampleMod/Content/ExampleModConfiguration.uasset and b/Mods/ExampleMod/Content/ExampleModConfiguration.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyArray.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyArray.uasset index 5ae73cd3d8..4dac746925 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyArray.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyArray.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyBool.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyBool.uasset index 71df0aa320..fdcbb97a09 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyBool.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyBool.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyClass.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyClass.uasset index fdde4e35b6..4e8cae161e 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyClass.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyClass.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyFloat.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyFloat.uasset index 599f556cda..ea543b0673 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyFloat.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyFloat.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyInteger.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyInteger.uasset index b23ddc5a01..d7289658d7 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyInteger.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyInteger.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertySection.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertySection.uasset index 126c91d53c..6bdca22a19 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertySection.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertySection.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyString.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyString.uasset index 4c39e0a62a..24ad2b222a 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyString.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/BP_ConfigPropertyString.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/BaseClasses/Widget_CP_Section_Base.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/BaseClasses/Widget_CP_Section_Base.uasset index 7791c4ebee..d002fbd52c 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/BaseClasses/Widget_CP_Section_Base.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/BaseClasses/Widget_CP_Section_Base.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CPA_Property_Container.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CPA_Property_Container.uasset index 5c310b3e40..53143a7eff 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CPA_Property_Container.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CPA_Property_Container.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CP_Container.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CP_Container.uasset index 5dec2b8248..e4393f49e8 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CP_Container.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/Container/Widget_CP_Container.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Array.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Array.uasset index 0dc268cd66..d334418e0f 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Array.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Array.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Class.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Class.uasset index 44453b2e39..b75bc6fec9 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Class.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Class.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_NumericText.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_NumericText.uasset index c04f27ed35..2f027afd69 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_NumericText.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_NumericText.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Horizontal.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Horizontal.uasset index 83e16420a6..8ef9a8d37c 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Horizontal.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Horizontal.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Vertical.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Vertical.uasset index ab74d9c46d..1b539cc0b5 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Vertical.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Section_Vertical.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Slider.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Slider.uasset index f1f019dd2b..affb5007b6 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Slider.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Slider.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_SpinBox.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_SpinBox.uasset index 60d59e5125..4f10400a91 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_SpinBox.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_SpinBox.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Text.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Text.uasset index dc5d4a9402..a2b2b254c1 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Text.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/ConfigProperties/Widgets/ValueWidgets/Widget_CP_Text.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_Mod.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_Mod.uasset index e0ecae6992..1aeb39af2d 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_Mod.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_Mod.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModConfig.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModConfig.uasset index 3e7a1bac4f..f81b2763e2 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModConfig.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModConfig.uasset differ diff --git a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModList.uasset b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModList.uasset index d5815b005d..7987f14d8d 100644 Binary files a/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModList.uasset and b/Mods/SML/Content/Interface/UI/Menu/Mods/Widget_ModList.uasset differ diff --git a/Mods/SML/Source/SML/Private/Configuration/ConfigManager.cpp b/Mods/SML/Source/SML/Private/Configuration/ConfigManager.cpp index 6e073907f3..10965475ca 100644 --- a/Mods/SML/Source/SML/Private/Configuration/ConfigManager.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/ConfigManager.cpp @@ -21,7 +21,7 @@ const TCHAR* SMLConfigModVersionField = TEXT("SML_ModVersion_DoNotChange"); void UConfigManager::ReloadModConfigurations() { UE_LOG(LogConfigManager, Display, TEXT("Reloading mod configurations...")); - + for (const TPair& Pair : Configurations) { LoadConfigurationInternal(Pair.Key, Pair.Value.RootValue, true); } @@ -29,20 +29,20 @@ void UConfigManager::ReloadModConfigurations() { void UConfigManager::SaveConfigurationInternal(const FConfigId& ConfigId) { const FRegisteredConfigurationData& ConfigurationData = Configurations.FindChecked(ConfigId); - + const URootConfigValueHolder* RootValue = ConfigurationData.RootValue; URawFormatValue* RawFormatValue = RootValue->GetWrappedValue()->Serialize(GetTransientPackage()); checkf(RawFormatValue, TEXT("Root RawFormatValue returned NULL for config %s"), *ConfigId.ModReference); - + //Root value should always be JsonObject, since root property is section property const TSharedPtr JsonValue = FJsonRawFormatConverter::ConvertToJson(RawFormatValue); check(JsonValue->Type == EJson::Object); TSharedRef UnderlyingObject = JsonValue->AsObject().ToSharedRef(); - + //Record mod version so we can keep file system file schema up to date FModInfo ModInfo; UModLoadingLibrary* ModLoadingLibrary = GetGameInstance()->GetSubsystem(); - + if (ModLoadingLibrary->GetLoadedModInfo(ConfigId.ModReference, ModInfo)) { const FString ModVersion = ModInfo.Version.ToString(); UnderlyingObject->SetStringField(SMLConfigModVersionField, ModVersion); @@ -57,7 +57,7 @@ void UConfigManager::SaveConfigurationInternal(const FConfigId& ConfigId) { const FString ConfigurationFilePath = GetConfigurationFilePath(ConfigId); //Make sure configuration directory exists FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*FPaths::GetPath(ConfigurationFilePath)); - + if (!FFileHelper::SaveStringToFile(JsonOutputString, *ConfigurationFilePath)) { UE_LOG(LogConfigManager, Error, TEXT("Failed to save configuration file to %s"), *ConfigurationFilePath); return; @@ -132,8 +132,8 @@ void UConfigManager::OnTimerManagerAvailable(FTimerManager* TimerManager) { } void UConfigManager::OnConfigMarkedDirty(FTimerManager* TimerManager) { - //Setup a timer which will force all changes into filesystem every in .2 seconds - + //Setup a timer which will force all changes into filesystem every in .2 seconds + // I dont like this .. Creating a UObject here .. // We want a Timer to Stop too frequent writing from incoming changes // some way to get world context from MarkConfigurationDirty would be needed to reliably use Timers for this @@ -154,7 +154,7 @@ void UConfigManager::ReinitializeCachedStructs(const FConfigId& ConfigId) { #if OPTIMIZE_FILL_CONFIGURATION_STRUCT const FRegisteredConfigurationData& ConfigurationData = Configurations.FindChecked(ConfigId); URootConfigValueHolder* RootConfigValue = ConfigurationData.RootValue; - + for (const TPair& Pair : ConfigurationData.CachedValues) { RootConfigValue->GetWrappedValue()->FillConfigStructSelf(Pair.Value); } @@ -191,12 +191,24 @@ void UConfigManager::FillConfigurationStruct(const FConfigId& ConfigId, const FD #endif } +bool UConfigManager::ResetToDefault(const FConfigId& ConfigId) { + UConfigPropertySection* UserRootSection = GetConfigurationRootSection(ConfigId); + if (!UserRootSection) { + return false; + } + bool bReset = UserRootSection->ResetToDefault(); + if (bReset) { + MarkConfigurationDirty(ConfigId); + } + return bReset; +} + UUserWidget* UConfigManager::CreateConfigurationWidget(const FConfigId& ConfigId, UUserWidget* Outer) { FRegisteredConfigurationData* ConfigurationData = Configurations.Find(ConfigId); if (ConfigurationData == NULL) { return NULL; } - + UConfigPropertySection* RootValue = ConfigurationData->RootValue->GetWrappedValue(); return RootValue->CreateEditorWidget(Outer); } @@ -207,14 +219,14 @@ void UConfigManager::ReplaceConfigurationClass(FRegisteredConfigurationData* Exi //Create new root section value from new configuration class URootConfigValueHolder* RootConfigValueHolder = ExistingData->RootValue; - + //Replace wrapped configuration section value with new root section, replace old configuration class RootConfigValueHolder->UpdateWrappedValue(NewConfiguration.GetDefaultObject()->RootSection); ExistingData->ConfigurationClass = NewConfiguration; //Populate new configuration with data from previous one RootConfigValueHolder->GetWrappedValue()->Deserialize(TempDataObject); - + //Refresh all cached struct values with new data ReinitializeCachedStructs(ExistingData->ConfigId); @@ -238,7 +250,7 @@ bool IsCompatibleConfigurationClassChange(UClass* OldConfigurationClass, UClass* } } #endif - + //Otherwise, replace is not compatible return false; } @@ -262,12 +274,12 @@ void UConfigManager::RegisterModConfiguration(TSubclassOf Con ReplaceConfigurationClass(ExistingData, Configuration); return; } - + //Create root value and wrap it into config root handling marking config dirty URootConfigValueHolder* RootConfigValueHolder = NewObject(this); RootConfigValueHolder->SetupRootValue(this, ConfigId); RootConfigValueHolder->UpdateWrappedValue(Configuration.GetDefaultObject()->RootSection); - + //Register configuration inside all of the internal properties Configurations.Add(ConfigId, FRegisteredConfigurationData{ConfigId, Configuration, RootConfigValueHolder}); @@ -287,7 +299,7 @@ UConfigPropertySection* UConfigManager::GetConfigurationRootSection(const FConfi void UConfigManager::Initialize(FSubsystemCollectionBase& Collection) { Collection.InitializeDependency(); - + //Subscribe to exit event so we make sure that pending saves are written to filesystem FCoreDelegates::OnPreExit.AddUObject(this, &UConfigManager::FlushPendingSaves); //Subscribe to timer manager availability delegate to be able to do periodic auto-saves diff --git a/Mods/SML/Source/SML/Private/Configuration/ConfigProperty.cpp b/Mods/SML/Source/SML/Private/Configuration/ConfigProperty.cpp index d75c568e8c..29ed2e63c1 100644 --- a/Mods/SML/Source/SML/Private/Configuration/ConfigProperty.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/ConfigProperty.cpp @@ -31,6 +31,43 @@ void UConfigProperty::FillConfigStruct_Implementation(const FReflectedObject& Re checkf(false, TEXT("FillConfigStruct not implemented")); } +bool UConfigProperty::ResetToDefault_Implementation() { + checkf(false, TEXT("ResetToDefault not implemented")); + return false; +} + +bool UConfigProperty::CanResetNow() const { + // Return false if this property does allow a user to reset it + if (!bAllowUserReset || !bParentSectionAllowsUserReset) { + return false; + } + // Return true if this property does not require a world reload + if (!bRequiresWorldReload) { + return true; + } + // Assume we can reset if for whatever reason we can't get the world + UWorld* World = GetWorld(); + if (!World) { + return true; + } + // Check whether or not the user is in the main menu since requires world reload is enabled + // GetAuthGameMode is a server-only function, but in the case we're not the server, then we know we're definitely not in the main menu anyways + if (AFGGameMode* GameMode = World->GetAuthGameMode()) { + return GameMode->IsMainMenuGameMode(); + } + return false; +} + +bool UConfigProperty::IsSetToDefaultValue_Implementation() const { + checkf(false, TEXT("IsSetToDefaultValue not implemented")); + return false; +} + +FString UConfigProperty::GetDefaultValueAsString_Implementation() const { + checkf(false, TEXT("GetDefaultValueAsString not implemented")); + return TEXT(""); +} + FConfigVariableDescriptor UConfigProperty::CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const { checkf(false, TEXT("CreatePropertyDescriptor not implemented")); return FConfigVariableDescriptor{}; diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyArray.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyArray.cpp index 1058fdda81..05d821c333 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyArray.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyArray.cpp @@ -1,4 +1,5 @@ #include "Configuration/Properties/ConfigPropertyArray.h" +#include "Configuration/Properties/ConfigPropertyFloat.h" #include "Configuration/CodeGeneration/ConfigVariableDescriptor.h" #include "Configuration/CodeGeneration/ConfigVariableLibrary.h" #include "Configuration/RawFileFormat/RawFormatValueArray.h" @@ -6,6 +7,27 @@ #define LOCTEXT_NAMESPACE "SML" +void UConfigPropertyArray::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValues from Values [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValues + // is set to the type default and Values is not, then Values has the old default value that we need to migrate. + if (DefaultValues.Num() == 0 && Values.Num() > 0) { + DefaultValues = Values; // Hand off the pointer, no need to duplicate. Values.Empty() below will not delete the objects + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Values.Empty(DefaultValues.Num()); + for (UConfigProperty* Property : DefaultValues) { + if (Property) { + UConfigProperty* Clone = DuplicateObject(Property, this); + Values.Add(Clone); + } else { + Values.Add(nullptr); + } + } +} + UConfigProperty* UConfigPropertyArray::AddNewElement() { checkf(DefaultValue, TEXT("Cannot add new element without default value defined")); UConfigProperty* NewValueProperty = NewObject(this, DefaultValue->GetClass(), NAME_None, RF_NoFlags, DefaultValue); @@ -52,7 +74,7 @@ EDataValidationResult UConfigPropertyArray::IsDataValid(TArray& Validatio FText::FromString(ConfigProperty ? ConfigProperty->GetClass()->GetName() : TEXT("None")))); ValidationResult = EDataValidationResult::Invalid; } - } + } return ValidationResult; } #endif @@ -80,9 +102,18 @@ void UConfigPropertyArray::Deserialize_Implementation(const URawFormatValue* Val //Just iterate raw format array and deserialize each of its items for (URawFormatValue* RawFormatValue : SerializedArray->GetUnderlyingArrayRef()) { UConfigProperty* AllocatedValue = AddNewElement(); + if (!bAllowUserReset || !bParentSectionAllowsUserReset) { + AllocatedValue->bParentSectionAllowsUserReset = false; + } AllocatedValue->Deserialize(RawFormatValue); } } + // Set default values to inherit Allow User Reset + for (UConfigProperty* Property : DefaultValues) { + if (Property && (!bAllowUserReset || !bParentSectionAllowsUserReset)) { + Property->bParentSectionAllowsUserReset = false; + } + } } void UConfigPropertyArray::FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const { @@ -95,8 +126,67 @@ void UConfigPropertyArray::FillConfigStruct_Implementation(const FReflectedObjec } } -void UConfigPropertyArray::HandleMarkDirty_Implementation() -{ +bool UConfigPropertyArray::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + Values.Empty(DefaultValues.Num()); + for (UConfigProperty* Property : DefaultValues) { + if (Property) { + UConfigProperty* Clone = DuplicateObject(Property, this); + Values.Add(Clone); + } else { + Values.Add(nullptr); + } + } + MarkDirty(); + return true; +} + +bool UConfigPropertyArray::IsSetToDefaultValue_Implementation() const { + if (Values.Num() != DefaultValues.Num()) { + return false; + } + for (int32 i = 0; i < Values.Num(); i++) { + const UConfigProperty* UserProperty = Values[i]; + const UConfigProperty* DefaultProperty = DefaultValues.IsValidIndex(i) ? DefaultValues[i] : nullptr; + if (!UserProperty || !DefaultProperty) { + return false; + } + // Check if the classes match and if the values are equal + if (UserProperty->GetClass() != DefaultProperty->GetClass()) { + return false; + } + // Special case for float properties to use FMath::IsNearlyEqual for comparison + if (const UConfigPropertyFloat* UserFloat = Cast(UserProperty)) { + const UConfigPropertyFloat* DefaultFloat = Cast(DefaultProperty); + if (!DefaultFloat || !FMath::IsNearlyEqual(UserFloat->Value, DefaultFloat->Value, SMALL_NUMBER)) { + return false; + } + } + // For other property types, compare describe values + else if (UserProperty->DescribeValue() != DefaultProperty->DescribeValue()) { + return false; + } + } + // MarkDirty(); + return true; +} + +FString UConfigPropertyArray::GetDefaultValueAsString_Implementation() const { + // A bit hacky, but Property->GetDefaultValueAsString() does not work here as the default + // values are not initialized within the DefaultValues array for whatever reason. + return FString::JoinBy(DefaultValues, TEXT(", "), [](const UConfigProperty* Property) -> FString { + if (!Property) { + return TEXT("null"); + } + const FString d = Property->DescribeValue(); + int32 i = d.Find(TEXT(" ")); + return (i == INDEX_NONE || d.Len() < 2) ? d : d.Mid(i + 1, d.Len() - i - 2); + }); +} + +void UConfigPropertyArray::HandleMarkDirty_Implementation() { MarkDirty(); } diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyBool.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyBool.cpp index 685ed26aa5..c7d7e08668 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyBool.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyBool.cpp @@ -4,8 +4,17 @@ #include "Configuration/RawFileFormat/RawFormatValueBool.h" #include "Reflection/BlueprintReflectedObject.h" -UConfigPropertyBool::UConfigPropertyBool() { - this->Value = false; +void UConfigPropertyBool::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValue from Value [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValue + // is set to the type default and Value is not, then Value has the old default value that we need to migrate. + if (DefaultValue == false && Value != DefaultValue) { + DefaultValue = Value; + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Value = DefaultValue; } FString UConfigPropertyBool::DescribeValue_Implementation() const { @@ -29,6 +38,23 @@ void UConfigPropertyBool::FillConfigStruct_Implementation(const FReflectedObject ReflectedObject.SetBoolProperty(*VariableName, Value); } +bool UConfigPropertyBool::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + Value = DefaultValue; + MarkDirty(); + return true; +} + +bool UConfigPropertyBool::IsSetToDefaultValue_Implementation() const { + return Value == DefaultValue; +} + +FString UConfigPropertyBool::GetDefaultValueAsString_Implementation() const { + return DefaultValue ? TEXT("true") : TEXT("false"); +} + FConfigVariableDescriptor UConfigPropertyBool::CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const { return UConfigVariableLibrary::MakeConfigVariablePrimitive(EConfigVariableType::ECVT_Bool); } diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyClass.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyClass.cpp index e77da7a3ab..a1b53eddf5 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyClass.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyClass.cpp @@ -7,11 +7,24 @@ #define LOCTEXT_NAMESPACE "SML" UConfigPropertyClass::UConfigPropertyClass() { - this->Value = NULL; + DefaultValue = NULL; BaseClass = UObject::StaticClass(); bLimitBaseClass = false; } +void UConfigPropertyClass::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValue from Value [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValue + // is set to the type default and Value is not, then Value has the old default value that we need to migrate. + if (DefaultValue == NULL && Value != DefaultValue) { + DefaultValue = Value; + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Value = DefaultValue; +} + bool UConfigPropertyClass::IsValidValueClass(UClass* Class) const { return Class ? (bLimitBaseClass && BaseClass ? Class->IsChildOf(BaseClass) : true) : bAllowNullValue; } @@ -66,6 +79,23 @@ void UConfigPropertyClass::FillConfigStruct_Implementation(const FReflectedObjec ReflectedObject.SetObjectProperty(*VariableName, Value); } +bool UConfigPropertyClass::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + SetClassValue(DefaultValue); + MarkDirty(); + return true; +} + +bool UConfigPropertyClass::IsSetToDefaultValue_Implementation() const { + return Value == DefaultValue; +} + +FString UConfigPropertyClass::GetDefaultValueAsString_Implementation() const { + return DefaultValue ? DefaultValue->GetName() : TEXT(""); +} + FConfigVariableDescriptor UConfigPropertyClass::CreatePropertyDescriptor_Implementation( UConfigGenerationContext* Context, const FString& OuterPath) const { return UConfigVariableLibrary::MakeConfigVariableClass(BaseClass); diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyFloat.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyFloat.cpp index 8f8252696b..cb34260d60 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyFloat.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyFloat.cpp @@ -4,8 +4,17 @@ #include "Configuration/RawFileFormat/RawFormatValueNumber.h" #include "Reflection/BlueprintReflectedObject.h" -UConfigPropertyFloat::UConfigPropertyFloat() { - this->Value = 0.0f; +void UConfigPropertyFloat::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValue from Value [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValue + // is set to the type default and Value is not, then Value has the old default value that we need to migrate. + if (FMath::IsNearlyZero(DefaultValue, SMALL_NUMBER) && !FMath::IsNearlyEqual(Value, DefaultValue, SMALL_NUMBER)) { + DefaultValue = Value; + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Value = DefaultValue; } FString UConfigPropertyFloat::DescribeValue_Implementation() const { @@ -29,6 +38,23 @@ void UConfigPropertyFloat::FillConfigStruct_Implementation(const FReflectedObjec ReflectedObject.SetFloatProperty(*VariableName, Value); } +bool UConfigPropertyFloat::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + Value = DefaultValue; + MarkDirty(); + return true; +} + +bool UConfigPropertyFloat::IsSetToDefaultValue_Implementation() const { + return FMath::IsNearlyEqual(Value, DefaultValue, SMALL_NUMBER); +} + +FString UConfigPropertyFloat::GetDefaultValueAsString_Implementation() const { + return FString::SanitizeFloat(DefaultValue); +} + FConfigVariableDescriptor UConfigPropertyFloat::CreatePropertyDescriptor_Implementation( UConfigGenerationContext* Context, const FString& OuterPath) const { return UConfigVariableLibrary::MakeConfigVariablePrimitive(EConfigVariableType::ECVT_Float); diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyInteger.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyInteger.cpp index 1aec2886a6..29ab7ac157 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyInteger.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyInteger.cpp @@ -4,8 +4,17 @@ #include "Configuration/RawFileFormat/RawFormatValueNumber.h" #include "Reflection/BlueprintReflectedObject.h" -UConfigPropertyInteger::UConfigPropertyInteger() { - this->Value = 0; +void UConfigPropertyInteger::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValue from Value [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValue + // is set to the type default and Value is not, then Value has the old default value that we need to migrate. + if (DefaultValue == 0 && Value != DefaultValue) { + DefaultValue = Value; + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Value = DefaultValue; } FString UConfigPropertyInteger::DescribeValue_Implementation() const { @@ -29,6 +38,23 @@ void UConfigPropertyInteger::FillConfigStruct_Implementation(const FReflectedObj ReflectedObject.SetIntProperty(*VariableName, Value); } +bool UConfigPropertyInteger::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + Value = DefaultValue; + MarkDirty(); + return true; +} + +bool UConfigPropertyInteger::IsSetToDefaultValue_Implementation() const { + return Value == DefaultValue; +} + +FString UConfigPropertyInteger::GetDefaultValueAsString_Implementation() const { + return FString::FromInt(DefaultValue); +} + FConfigVariableDescriptor UConfigPropertyInteger::CreatePropertyDescriptor_Implementation( UConfigGenerationContext* Context, const FString& OuterPath) const { return UConfigVariableLibrary::MakeConfigVariablePrimitive(EConfigVariableType::ECVT_Int32); diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertySection.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertySection.cpp index 18fc35acd6..d40361f414 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertySection.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertySection.cpp @@ -33,6 +33,9 @@ void UConfigPropertySection::Deserialize_Implementation(const URawFormatValue* V for (const TPair& Property : SectionProperties) { const URawFormatValue* RawChildValue = ObjectValue->GetValue(Property.Key); if (Property.Value != NULL && RawChildValue != NULL) { + if (!bAllowUserReset || !bParentSectionAllowsUserReset) { + Property.Value->bParentSectionAllowsUserReset = false; + } Property.Value->Deserialize(RawChildValue); } } @@ -94,8 +97,55 @@ void UConfigPropertySection::FillConfigStruct_Implementation(const FReflectedObj ReflectedObject.SetStructProperty(*VariableName, ChildObject); } -void UConfigPropertySection::HandleMarkDirty_Implementation() -{ +bool UConfigPropertySection::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + bool bAnyReset = (SectionProperties.Num() == 0); + for (const TPair& Pair : SectionProperties) { + UConfigProperty* Property = Pair.Value; + if (Property) { + bool bResetThisOne = Property->ResetToDefault(); + bAnyReset = bAnyReset || bResetThisOne; + } + } + if (bAnyReset) { + MarkDirty(); + } + return bAnyReset; +} + +bool UConfigPropertySection::IsSetToDefaultValue_Implementation() const { + for (const TPair& Pair : SectionProperties) { + UConfigProperty* Property = Pair.Value; + if (IsValid(Property) && !Property->IsSetToDefaultValue()) { + return false; + } + } + return true; +} + +bool UConfigPropertySection::HasResettableChildProperty(bool bIncludeHidden = true) const { + for (const TPair& Pair : SectionProperties) { + UConfigProperty* Property = Pair.Value; + if (!IsValid(Property)) { + continue; + } + if (!bIncludeHidden && Property->bHidden) { + continue; + } + if (Property->CanResetNow() && !Property->IsSetToDefaultValue()) { + return true; + } + } + return false; +} + +FString UConfigPropertySection::GetDefaultValueAsString_Implementation() const { + return TEXT(""); +} + +void UConfigPropertySection::HandleMarkDirty_Implementation() { MarkDirty(); } diff --git a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyString.cpp b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyString.cpp index 0015990930..17152afa3a 100644 --- a/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyString.cpp +++ b/Mods/SML/Source/SML/Private/Configuration/Properties/ConfigPropertyString.cpp @@ -3,8 +3,17 @@ #include "Configuration/RawFileFormat/RawFormatValueString.h" #include "Reflection/BlueprintReflectedObject.h" -UConfigPropertyString::UConfigPropertyString() { - this->Value = TEXT(""); +void UConfigPropertyString::PostLoad() { + Super::PostLoad(); + // Migrate to DefaultValue from Value [Remove only this if statement once migration is no longer needed] + // PostLoad runs before the user config is deserialized, but it still has the values from the CDO. If DefaultValue + // is set to the type default and Value is not, then Value has the old default value that we need to migrate. + if (DefaultValue.IsEmpty() && !Value.IsEmpty()) { + DefaultValue = Value; + } + // Set initial value to default value. This runs before the user config is deserialized + // and ensures that if the user has never set a value, it's set to the default. + Value = DefaultValue; } FString UConfigPropertyString::DescribeValue_Implementation() const { @@ -28,6 +37,22 @@ void UConfigPropertyString::FillConfigStruct_Implementation(const FReflectedObje ReflectedObject.SetStrProperty(*VariableName, Value); } +bool UConfigPropertyString::ResetToDefault_Implementation() { + if (!CanResetNow()) { + return false; + } + Value = DefaultValue; + MarkDirty(); + return true; +} + +bool UConfigPropertyString::IsSetToDefaultValue_Implementation() const { + return Value == DefaultValue; +} + +FString UConfigPropertyString::GetDefaultValueAsString_Implementation() const { + return *DefaultValue.ReplaceQuotesWithEscapedQuotes(); +} FConfigVariableDescriptor UConfigPropertyString::CreatePropertyDescriptor_Implementation( UConfigGenerationContext* Context, const FString& OuterPath) const { diff --git a/Mods/SML/Source/SML/Public/Configuration/ConfigManager.h b/Mods/SML/Source/SML/Public/Configuration/ConfigManager.h index 787e7577db..8b2e8e38c2 100644 --- a/Mods/SML/Source/SML/Public/Configuration/ConfigManager.h +++ b/Mods/SML/Source/SML/Public/Configuration/ConfigManager.h @@ -55,11 +55,15 @@ class SML_API UConfigManager : public UGameInstanceSubsystem { /** Marks configuration as dirty and pending save */ UFUNCTION(BlueprintCallable) void MarkConfigurationDirty(const FConfigId& ConfigId); - + /** Fills passed struct with a data obtained from active configuration identified by passed config id */ UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, CustomThunk, meta = (CustomStructureParam = "StructInfo")) void FillConfigurationStruct(const FConfigId& ConfigId, UPARAM(Ref) const FDynamicStructInfo& StructInfo); + /** Resets mod configuration to its default values */ + UFUNCTION(BlueprintCallable) + bool ResetToDefault(const FConfigId& ConfigId); + /** Creates a configuration widget hierarchy for active configuration specified by passed id */ UFUNCTION(BlueprintCallable, meta = (DefaultToSelf = "Outer")) UUserWidget* CreateConfigurationWidget(const FConfigId& ConfigId, UUserWidget* Outer); @@ -77,7 +81,7 @@ class SML_API UConfigManager : public UGameInstanceSubsystem { UConfigPropertySection* GetConfigurationRootSection(const FConfigId& ConfigId) const; void Initialize(FSubsystemCollectionBase& Collection) override; - + /** Returns configuration folder path used by config manager */ static FString GetConfigurationFolderPath(); private: @@ -87,7 +91,7 @@ class SML_API UConfigManager : public UGameInstanceSubsystem { static FString GetConfigurationFilePath(const FConfigId& ConfigId); void ReplaceConfigurationClass(FRegisteredConfigurationData* ExistingData, TSubclassOf NewConfiguration); - + void OnTimerManagerAvailable(class FTimerManager* TimerManager); void OnConfigMarkedDirty(FTimerManager* TimerManager); @@ -103,7 +107,7 @@ class SML_API UConfigManager : public UGameInstanceSubsystem { /** Array of all configurations pending save */ TArray PendingSaveConfigurations; - + /** Registered configurations */ UPROPERTY() TMap Configurations; @@ -114,7 +118,7 @@ class SML_API UConfigManager : public UGameInstanceSubsystem { P_FINISH; P_THIS->FillConfigurationStruct(ConfigId, StructInfo); } - - + + FTimerHandle SaveToDiskTimerHandle; }; diff --git a/Mods/SML/Source/SML/Public/Configuration/ConfigProperty.h b/Mods/SML/Source/SML/Public/Configuration/ConfigProperty.h index 6890670288..68061c255d 100644 --- a/Mods/SML/Source/SML/Public/Configuration/ConfigProperty.h +++ b/Mods/SML/Source/SML/Public/Configuration/ConfigProperty.h @@ -1,5 +1,6 @@ #pragma once #include "CoreMinimal.h" +#include "FGGameMode.h" #include "UObject/Object.h" #include "Configuration/CodeGeneration/ConfigVariableDescriptor.h" #include "Reflection/BlueprintReflectedObject.h" @@ -28,14 +29,24 @@ class SML_API UConfigProperty : public UObject { UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property", meta = (MultiLine = true)) FText Tooltip; - /** Whenever this value is only editable from main menu and disabled for editing in pause menu */ + /** If this value is only editable from the main menu -> can't be edited from the pause menu when loaded into a game world */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") uint8 bRequiresWorldReload: 1; - /** Whenever this value should be hidden in Widgets ( No User Input ) */ + /** If this value should not be displayed in Config Widgets, meaning the user can't see it to configure it (unless they manually edit the config file on disk) */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") uint8 bHidden : 1; + /** If true, this property can be reset by the user from the Config Widget. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") + bool bAllowUserReset = true; + + /** Cached. If the parent section allows this property to be reset. It is up to container properties to implement setting this value. */ + UPROPERTY(BlueprintReadOnly, Transient, Category="Internal") + bool bParentSectionAllowsUserReset = true; + + +public: /** Describes value of this property for debugging purposes */ UFUNCTION(BlueprintPure, BlueprintNativeEvent) FString DescribeValue() const; @@ -54,7 +65,7 @@ class SML_API UConfigProperty : public UObject { UPROPERTY(BlueprintAssignable) FOnPropertyValueChanged OnPropertyValueChanged; - + /** Creates widget instance for editing this configuration property's value. Can return NULL if property doesn't support direct UI editing */ UFUNCTION(BlueprintPure, BlueprintNativeEvent, meta = (DefaultToSelf = "ParentWidget")) UUserWidget* CreateEditorWidget(class UUserWidget* ParentWidget) const; @@ -67,6 +78,22 @@ class SML_API UConfigProperty : public UObject { UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure = false) void FillConfigStruct(const FReflectedObject& ReflectedObject, const FString& VariableName) const; + /** Resets this property to its default value */ + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Configuration Property") + bool ResetToDefault(); + + /** Returns true if this property is currently able to be reset by the user */ + UFUNCTION(BlueprintCallable, Category="Configuration Property") + bool CanResetNow() const; + + /** Returns true if this property's current value is equal to its default value */ + UFUNCTION(BlueprintPure, BlueprintNativeEvent, Category="Configuration Property") + bool IsSetToDefaultValue() const; + + /** Returns the default value as a string */ + UFUNCTION(BlueprintPure, BlueprintNativeEvent, Category="Configuration Property") + FString GetDefaultValueAsString() const; + private: /** The Serialize() definition above shadows the native UObject::Serialize. Declare that we want to keep the UBOject implementation. */ using UObject::Serialize; diff --git a/Mods/SML/Source/SML/Public/Configuration/ModConfiguration.h b/Mods/SML/Source/SML/Public/Configuration/ModConfiguration.h index d58bc3167c..ebd8d34f2a 100644 --- a/Mods/SML/Source/SML/Public/Configuration/ModConfiguration.h +++ b/Mods/SML/Source/SML/Public/Configuration/ModConfiguration.h @@ -6,7 +6,7 @@ USTRUCT(BlueprintType) struct SML_API FConfigId { GENERATED_BODY(); -public: +public: /** Mod reference of the requested configuration owner */ UPROPERTY(BlueprintReadWrite, EditAnywhere) FString ModReference; @@ -33,11 +33,11 @@ class SML_API UModConfiguration : public UObject { /** Id for configuration described by this class */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) FConfigId ConfigId; - + /** Display name of this configuration, as it will be visible to user */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) FText DisplayName; - + /** Description of this configuration, can be empty */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) FText Description; @@ -53,4 +53,4 @@ class SML_API UModConfiguration : public UObject { #if WITH_EDITOR virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; #endif -}; +}; \ No newline at end of file diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyArray.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyArray.h index e55b03056f..859c557a78 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyArray.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyArray.h @@ -8,14 +8,19 @@ UCLASS() class SML_API UConfigPropertyArray : public UConfigProperty, public IConfigValueDirtyHandlerInterface { GENERATED_BODY() public: - /** Defines "template" default value used for allocating other values in the array */ - UPROPERTY(EditAnywhere, Instanced, BlueprintReadOnly, Category = "Configuration Property") + /** Defines the "template" default value used for allocating other values in the array */ + UPROPERTY(EditAnywhere, Instanced, BlueprintReadOnly, Category = "Configuration Property", meta = (DisplayName= "New Value Template")) UConfigProperty* DefaultValue; - - /** Current values of this configuration property. Should be of the same type as DefaultValue, names don't matter */ + + /** Default values of this configuration property. This is the value the property resets to. Should be of the same type as DefaultValue, names don't matter */ UPROPERTY(EditAnywhere, Instanced, BlueprintReadOnly, Category = "Configuration Property") + TArray DefaultValues; + + /** Runtime values of this configuration property. */ + UPROPERTY(Instanced, BlueprintReadWrite, Category = "Configuration Property") TArray Values; +public: /** Allocates new default element and inserts it at the specified index */ UFUNCTION(BlueprintCallable) UConfigProperty* AddNewElement(); @@ -31,16 +36,21 @@ class SML_API UConfigPropertyArray : public UConfigProperty, public IConfigValue //Begin UObject #if WITH_EDITOR virtual bool CanEditChange(const FProperty* InProperty) const override; - virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; + virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; #endif //End UObject + virtual void PostLoad() override; + //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; virtual URawFormatValue* Serialize_Implementation(UObject* Outer) const override; virtual void Deserialize_Implementation(const URawFormatValue* Value) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty //Begin IConfigValueDirtyHandlerInterface diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyBool.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyBool.h index a9a85117dd..adaf113186 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyBool.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyBool.h @@ -6,11 +6,16 @@ UCLASS() class SML_API UConfigPropertyBool : public UConfigProperty { GENERATED_BODY() public: - /** Current value of this configuration property */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Configuration Property") + /** Default value of this configuration property. This is the value the property resets to. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") + bool DefaultValue; + + /** Runtime value of this configuration property. */ + UPROPERTY(BlueprintReadWrite, Category = "Configuration Property") bool Value; - - UConfigPropertyBool(); + +public: + virtual void PostLoad() override; //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; @@ -18,5 +23,8 @@ class SML_API UConfigPropertyBool : public UConfigProperty { virtual void Deserialize_Implementation(const URawFormatValue* Value) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty }; \ No newline at end of file diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyClass.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyClass.h index 6d0018a0fc..0d990fa73c 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyClass.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyClass.h @@ -9,7 +9,7 @@ class SML_API UConfigPropertyClass : public UConfigProperty { /** True if we should limit which classes can be set to this property values by particular base class type */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") bool bLimitBaseClass; - + /** Base class that values of this type should have */ UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (EditCondition = "bLimitBaseClass",AllowAbstract = "true"), Category = "Configuration Property") UClass* BaseClass; @@ -18,12 +18,17 @@ class SML_API UConfigPropertyClass : public UConfigProperty { UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") bool bAllowNullValue; - /** Current value of this class property. DO NOT SET DIRECTLY, USE SetClassValue */ + /** Default value of this configuration property. This is the value the property resets to. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowAbstract = "true"), Category = "Configuration Property") + UClass* DefaultValue; + + /** Runtime value of this configuration property. DO NOT SET DIRECTLY, USE SetClassValue */ + UPROPERTY(BlueprintReadOnly, meta = (AllowAbstract = "true"), Category = "Configuration Property") UClass* Value; +public: UConfigPropertyClass(); - + /** Returns true if this class is a valid value for this property */ UFUNCTION(BlueprintPure) bool IsValidValueClass(UClass* Class) const; @@ -37,12 +42,17 @@ class SML_API UConfigPropertyClass : public UConfigProperty { virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; #endif //End UObject - + + virtual void PostLoad() override; + //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; virtual URawFormatValue* Serialize_Implementation(UObject* Outer) const override; virtual void Deserialize_Implementation(const URawFormatValue* RawValue) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty }; \ No newline at end of file diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyFloat.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyFloat.h index 9a519db91a..4258dbefcb 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyFloat.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyFloat.h @@ -6,11 +6,16 @@ UCLASS() class SML_API UConfigPropertyFloat : public UConfigProperty { GENERATED_BODY() public: - /** Current value of this configuration property */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Configuration Property") + /** Default value of this configuration property. This is the value the property resets to. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") + float DefaultValue; + + /** Runtime value of this configuration property. */ + UPROPERTY(BlueprintReadWrite, Category = "Configuration Property") float Value; - - UConfigPropertyFloat(); + +public: + virtual void PostLoad() override; //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; @@ -18,5 +23,8 @@ class SML_API UConfigPropertyFloat : public UConfigProperty { virtual void Deserialize_Implementation(const URawFormatValue* Value) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty }; \ No newline at end of file diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyInteger.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyInteger.h index 276ed89990..d01c68b7e3 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyInteger.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyInteger.h @@ -6,17 +6,25 @@ UCLASS() class SML_API UConfigPropertyInteger : public UConfigProperty { GENERATED_BODY() public: - /** Current value of this configuration property */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Configuration Property") + /** Default value of this configuration property. This is the value the property resets to. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") + int32 DefaultValue; + + /** Runtime value of this configuration property. */ + UPROPERTY(BlueprintReadWrite, Category = "Configuration Property") int32 Value; - - UConfigPropertyInteger(); - + +public: + virtual void PostLoad() override; + //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; virtual URawFormatValue* Serialize_Implementation(UObject* Outer) const override; virtual void Deserialize_Implementation(const URawFormatValue* Value) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty }; diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertySection.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertySection.h index 3fc62c3572..d240e37d29 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertySection.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertySection.h @@ -17,10 +17,14 @@ class SML_API UConfigPropertySection : public UConfigProperty, public IConfigVal /** Fills reflected object with the values of this section */ void FillConfigStructSelf(const FReflectedObject& ReflectedObject) const; - + + /** Checks if any child properties can be reset. */ + UFUNCTION(BlueprintCallable, Category="Configuration Property") + bool HasResettableChildProperty(bool bIncludeHidden) const; + //Begin UObject #if WITH_EDITOR - virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; + virtual EDataValidationResult IsDataValid(TArray& ValidationErrors) override; #endif //End UObject @@ -30,6 +34,9 @@ class SML_API UConfigPropertySection : public UConfigProperty, public IConfigVal virtual void Deserialize_Implementation(const URawFormatValue* Value) override; FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty //Begin IConfigValueDirtyHandlerInterface diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyString.h b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyString.h index 9422c3dc4e..6501d09b99 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyString.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/ConfigPropertyString.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Configuration/ConfigProperty.h" #include "ConfigPropertyString.generated.h" @@ -6,17 +6,25 @@ UCLASS() class SML_API UConfigPropertyString : public UConfigProperty { GENERATED_BODY() public: - /** Current value of this configuration property */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Configuration Property") + /** Default value of this configuration property. This is the value the property resets to. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration Property") + FString DefaultValue; + + /** Runtime value of this configuration property. */ + UPROPERTY(BlueprintReadWrite, Category = "Configuration Property") FString Value; - - UConfigPropertyString(); - + +public: + virtual void PostLoad() override; + //Begin UConfigProperty virtual FString DescribeValue_Implementation() const override; virtual URawFormatValue* Serialize_Implementation(UObject* Outer) const override; virtual void Deserialize_Implementation(const URawFormatValue* Value) override; virtual FConfigVariableDescriptor CreatePropertyDescriptor_Implementation(UConfigGenerationContext* Context, const FString& OuterPath) const override; virtual void FillConfigStruct_Implementation(const FReflectedObject& ReflectedObject, const FString& VariableName) const override; + virtual bool ResetToDefault_Implementation() override; + virtual bool IsSetToDefaultValue_Implementation() const override; + virtual FString GetDefaultValueAsString_Implementation() const override; //End UConfigProperty }; \ No newline at end of file diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Array.h b/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Array.h index 3a1f7cae28..86f2a64f4a 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Array.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Array.h @@ -19,4 +19,7 @@ class SML_API UCP_Array : public UConfigPropertyArray { UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Default", meta = (DisplayAfter = "DefaultValue")) ECP_ArrayWidgetType WidgetType; + UPROPERTY(Transient, BlueprintReadWrite, Category = "Default", meta = (DisplayAfter = "DefaultValue")) + bool Collapsed; + }; diff --git a/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Section.h b/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Section.h index 9941516fdf..7baf5be394 100644 --- a/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Section.h +++ b/Mods/SML/Source/SML/Public/Configuration/Properties/WidgetExtension/CP_Section.h @@ -6,20 +6,23 @@ UENUM(Blueprinttype) enum class ECP_SectionWidgetType : uint8 { - CPS_Horizontal UMETA(Displayname = "Horizontal List", Tooltip = "Nested Properties will be Listed in a Horizontal Layout"), - CPS_Vertical UMETA(Displayname = "Vertical List", Tooltip = "Nested Properties will be Listed in a Vertical Layout"), + CPS_Horizontal UMETA(Displayname = "Horizontal List", Tooltip = "Nested Properties will be presented in a Horizontal Layout (scroll bar used if needed)"), + CPS_Vertical UMETA(Displayname = "Vertical List", Tooltip = "Nested Properties will be presented in a Vertical Layout"), }; UCLASS(EditInlineNew, Abstract) class SML_API UCP_Section : public UConfigPropertySection { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, Category = "Default", BlueprintReadOnly, meta = (DisplayAfter = "DefaultValue")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Default", meta = (DisplayAfter = "DefaultValue")) ECP_SectionWidgetType WidgetType; - UPROPERTY(EditAnywhere, Category = "Default", BlueprintReadOnly, meta = (DisplayAfter = "DefaultValue")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Default", meta = (DisplayAfter = "DefaultValue")) bool HasHeader; - UPROPERTY(EditAnywhere,Category= "Default", BlueprintReadOnly, meta = (DisplayAfter = "DefaultValue")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category= "Default", meta = (DisplayAfter = "DefaultValue")) FText HeaderText; + UPROPERTY(Transient, BlueprintReadWrite, Category = "Default", meta = (DisplayAfter = "DefaultValue")) + bool Collapsed; + }; \ No newline at end of file