Disabling Built-In GameFeatureData

When adding game feature actions, I was wondering what your thoughts were wrt. exposing the ability to disallow certain actions from appearing?

For context, in GameFeature.h and GameFeatureData.cpp, there is a `GetDisallowedActions` method that removes a certain class from the dropdown list. This can actually be super helpful if we plan on building our own custom game feature actions that are close, but not the same, to the built-in engine ones. In the example below, pretend I want to disable the built in `UGameFeatureAction_AddCheats`, this would be the fastest way to do that… but it’s not ideal. Maybe some kind of config or other option to do this?

What do you think?

	/** List of actions to perform as this game feature is loaded/activated/deactivated/unloaded */
	UPROPERTY(EditDefaultsOnly, Instanced, Category="Game Feature | Actions", meta = (GetDisallowedClasses = "GetDisallowedActions"))
	TArray<TObjectPtr<UGameFeatureAction>> Actions;

...

TArray<UClass*> UGameFeatureData::GetDisallowedActions() const
{
	TArray<UClass*> DisallowedClasses;

	if (!GetDefault<UEditorExperimentalSettings>()->bEnableWorldPartitionExternalDataLayers)
	{
		DisallowedClasses.Add(UGameFeatureAction_AddWorldPartitionContent::StaticClass());
	}

	// <ZM> #DebugGFPs - I've created my own action similar to 'add cheats' that I want my developers to use
	// to that effect, I'd like the built-in action to not show up in the dropdown
	DisallowedClasses.Add(UGameFeatureAction_AddCheats::StaticClass());
	// </ZM>

	return DisallowedClasses;
}




Hey there, first of all I think it’s reasonable to want to disallow some actions. When you prefer a project-specific subclass or alternative, that’s a valid use case.

One problem is that the engine has many places with class pickers and it’s untenable to add configurability for all of them. That engine modification you made is perfectly fine.

An alternative is to create an editor validator for it, so placing this in the project will popup errors when a developer tries to add GFA_AddCheats to a GameFeatureData asset, when saving:

#pragma once
 
#include "CoreMinimal.h"
#include "EditorValidatorBase.h"
#include "GameFeatureData.h"
#include "GameFeatureAction_AddCheats.h"
#include "Misc/DataValidation.h"
#include "MyGFPValidatorBase.generated.h"
 
UCLASS()
class MYGAME_API UMyGFPValidatorBase : public UEditorValidatorBase
{
	GENERATED_BODY()
	
	bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const override
	{
		return InObject->IsA<UGameFeatureData>();
	}
 
	EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) override
	{
		if (UGameFeatureData* GameFeature = Cast<UGameFeatureData>(InAsset))
		{
			const TArray<UGameFeatureAction*>& Actions = GameFeature->GetActions();
			for (UGameFeatureAction* Action : Actions)
			{
				if (Action && Action->GetClass() == UGameFeatureAction_AddCheats::StaticClass())
				{
					const FText WrongAddCheatsError = NSLOCTEXT("UMyGFPValidatorBase", "WrongAddCheats", "GameFeatureData '{GameFeatureName}' contains UGameFeatureAction_AddCheats. Use UGameFeatureAction_MyCustomAddCheats instead!");
					FFormatNamedArguments Args;
					Args.Add("GameFeatureName", FText::FromString(GameFeature->GetName()));
					Context.AddError(FText::Format(WrongAddCheatsError, Args));
					return EDataValidationResult::Invalid;
				}
			}
		}
		return EDataValidationResult::Valid;
	}
 
};

This approach does mean the developer only gets the error after having already added it, so DisallowedClasses has an advantage of even preventing the user error. I’ll forward this feedback to the system owner of GFPs to consider support for configurable DisallowedClasses. But consider the validator approach as well since it’s one less engine modification to maintain for your team.

After discussion with the Game Feature Plugin system owner: we’re not making DisallowedActions here INI Config-able, but we have made GetDisallowedActions() virtual so any UGameFeatureData subclass can override it. It’s in //UE5/Main now at CL 47268069.