DataConfigEditorExtra
Module DataConfigEditorExtra
contains examples that need to be run in a editor context.
DcEditorExtraModule
This is a good reference of integrating DataConfigCore
in a editor module. Here's a checklist:
- Register additional diagnostics early.
- Call
DcStartUp()/DcShutDonw()
pair. - Register custom diagnostic consumer.
FDcMessageLogDiagnosticConsumer
is an example of redirecting diagnostics into UE Message Log window with its own category.
Deserialize Blueprint Class Instances
The Property System is so powerful that you can create new Blueprint Class, which is equivalent to C++ UCLASS
to some extents, within the Blueprint Editor. In this example we implemented deserializing these.
Blueprint Class can be referenced by the blueprint asset path:
// DataConfig/Source/DataConfigEditorExtra/Private/DataConfig/EditorExtra/Deserialize/DcDeserializeBPClass.cpp
FString Str = TEXT(R"(
{
//...
"ClassField3" : "/DataConfig/DcFixture/DcTestBlueprintClassBeta",
}
)");
UTEST_TRUE("...", Dest.ClassField3->GetFName() == TEXT("DcTestBlueprintClassBeta_C"));
And Blueprint structs can also be deserialized from JSON. We need to rewrite the handler HandlerBPDcAnyStructDeserialize
for looking up Blueprint Struct by path:
// DataConfig/Source/DataConfigEditorExtra/Private/DataConfig/EditorExtra/Deserialize/DcDeserializeBPClass.cpp
FString Str = TEXT(R"(
{
"AnyStructField1" : {
"$type" : "/DataConfig/DcFixture/DcTestBlueprintStructWithColor",
"NameField" : "Foo",
"StrField" : "Bar",
"IntField" : 123,
"ColorField" : "#FF0000FF"
}
}
)");
There's a quirk that Blueprint Struct actually mangle its field names. The struct above dumps to something like this:
-----------------------------------------
# Datum: 'UserDefinedStruct', 'DcTestBlueprintStructWithColor'
<StructRoot> 'DcTestBlueprintStructWithColor'
|---<Name> 'NameField_5_97BFF114405C1934C2F33E8668BF1652'
|---<Name> 'Foo'
|---<Name> 'StrField_9_FAA71EFE4896F4E6B1478B9C13B2CE52'
|---<String> 'Bar'
|---<Name> 'IntField_11_3BC7CB0F42439CE2196F7AA82A1AC374'
|---<Int32> '123'
|---<Name> 'ColorField_14_F676BCF245B2977B678B65A8216E94EB'
|---<StructRoot> 'Color'
| |---<Name> 'B'
| |---<UInt8> '0'
| |---<Name> 'G'
| |---<UInt8> '0'
| |---<Name> 'R'
| |---<UInt8> '255'
| |---<Name> 'A'
| |---<UInt8> '255'
|---<StructEnd> 'Color'
<StructEnd> 'DcTestBlueprintStructWithColor'
-----------------------------------------
The good news is that DataConfig already got this covered.
Deserialize GameplayTags
GameplayTags is a built-in runtime module that implements hierarchical tags. In this example we implemented deserializing into FGameplayTag
from a string.
// DataConfig/Source/DataConfigEditorExtra/Private/DataConfig/EditorExtra/Deserialize/DcDeserializeGameplayTags.cpp
FString Str = TEXT(R"(
{
"TagField1" : null,
"TagField2" : "DataConfig.Foo.Bar"
}
)");
UTEST_FALSE("...", Dest.TagField1.IsValid());
UTEST_TRUE("...", Dest.TagField2.IsValid());
UTEST_TRUE("...", Dest.TagField2 == UGameplayTagsManager::Get().RequestGameplayTag(TEXT("DataConfig.Foo.Bar")));
We also implemented deserializing FGameplayTagContainer
from a list of strings:
// DataConfig/Source/DataConfigEditorExtra/Private/DataConfig/EditorExtra/Deserialize/DcDeserializeGameplayTags.cpp
FString Str = TEXT(R"(
{
"TagContainerField1" : [],
"TagContainerField2" : [
"DataConfig.Foo.Bar",
"DataConfig.Foo.Bar.Baz",
"DataConfig.Tar.Taz",
]
}
)");
UTEST_TRUE("...", Dest.TagContainerField1.Num() == 0);
UTEST_TRUE("...", Dest.TagContainerField2.Num() == 3);
UTEST_TRUE("...", Dest.TagContainerField2.HasTagExact(
UGameplayTagsManager::Get().RequestGameplayTag(TEXT("DataConfig.Foo.Bar"))
));
UTEST_TRUE("...", Dest.TagContainerField2.HasTagExact(
UGameplayTagsManager::Get().RequestGameplayTag(TEXT("DataConfig.Foo.Bar.Baz"))
));
UTEST_TRUE("...", Dest.TagContainerField2.HasTagExact(
UGameplayTagsManager::Get().RequestGameplayTag(TEXT("DataConfig.Tar.Taz"))
));
Note that gameplay tag parsing has error reporting built-in. In this case we can pipe it into our diagnostic:
// DataConfig/Source/DataConfigEditorExtra/Private/DataConfig/EditorExtra/Deserialize/DcDeserializeGameplayTags.cpp
static FDcResult _StringToGameplayTag(FDcDeserializeContext& Ctx, const FString& Str, FGameplayTag* OutTagPtr)
{
FString FixedString;
FText Err;
if (!FGameplayTag::IsValidGameplayTagString(Str, &Err, &FixedString))
{
return DC_FAIL(DcDEditorExtra, InvalidGameplayTagStringFixErr)
<< Str << FixedString << Err;
}
//...
}
In case of a invalid tag it would report the reason and fixed string:
# DataConfig Error: Invalid Gameplay Tag String, Actual: 'DataConfig.Invalid.Tag.', Fixed: 'DataConfig.Invalid.Tag', Error: 'Tag ends with .'
- [JsonReader] --> <in-memory>5:4
3 | {
4 | "TagField1" : null,
5 | "TagField2" : "DataConfig.Invalid.Tag."
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6 | }
7 |
- [PropertyWriter] Writing property: (FDcEditorExtraTestStructWithGameplayTag1)$root.(FGameplayTag)TagField2
[C:\DevUE\UnrealEngine\Engine\Source\Developer\MessageLog\Private\Model\MessageLogListingModel.cpp(73)]
Deserialize Gameplay Abilities
We'll conclude examples with a concrete user story: populating GameplayAbility
and GameplayEffect
blueprint from JSON file.
Gameplay Ability System is a built-in plugin for building data driven abilities. Users are expected to derived and modify GameplayAbility
and GameplayEffect
blueprint for custom logic.
Given a JSON like this:
// DataConfig/Tests/Fixture_AbilityAlpha.json
{
/// Tags
"AbilityTags" : [
"DataConfig.Foo.Bar",
"DataConfig.Foo.Bar.Baz",
],
"CancelAbilitiesWithTag" : [
"DataConfig.Foo.Bar.Baz",
"DataConfig.Tar.Taz",
],
/// Costs
"CostGameplayEffectClass" : "/DataConfig/DcFixture/DcTestGameplayEffectAlpha",
/// Advanced
"ReplicationPolicy" : "ReplicateYes",
"InstancingPolicy" : "NonInstanced",
}
Right click on a GameplayAbility
blueprint asset and select Load From JSON
, then select this file and confirm. It would correctly populate the fields with the values in JSON, as seen in the pic below:
Most of the logic is in DataConfig/EditorExtra/Deserialize/DcDeserializeGameplayAbility.cpp
:
- The context menu is added from
GameplayAbilityEffectExtender
. There's another handy item namedDump To Log
which dumps any blueprint CDO into the log. - DataConfig deserializer is setup in
LazyInitializeDeserializer()
. We added custom logic for deserializingFGameplayAttribute
from a string likeDcTestAttributeSet.Mana
. - We also reused many methods from previous examples to support
FGameplayTag
deserialization and Blueprint class look up by path.