treehouse/content/programming/technologies/unreal-engine/fixes.tree
2024-03-08 17:06:47 +01:00

123 lines
5.9 KiB
Plaintext

%% title = "new in Unreal Engine 5.4: data validation quick fixes"
thumbnail.id = "01HP1G5WC29GP4KQY1NV1F1RR1"
thumbnail.alt = "a screenshot with a validation error in it; beneath the error there's a hint that it may be fixed automatically, coupled with a link you can click on to fix the issue"
% id = "01HP1FESY3H9K1QVSM1XMNC8NS"
- a few days ago I got a really cool change into Unreal, which allows you to add quick fixes to any data validation warning/error you emit:
![a screenshot with a validation error in it; beneath the error there's a hint that it may be fixed automatically, coupled with a link you can click on to fix the issue][pic:01HP1G5WC29GP4KQY1NV1F1RR1]
% id = "01HP1FESY3K11EJJ9NSFBDCTWS"
+ you can get this change in...
% id = "01HP1FESY3CP8AJ00AQ9B6FQB2"
- [Git commit `0a6de35a5beb8534e19579cf1058460b4eb2bc79`](https://github.com/EpicGames/UnrealEngine/commit/0a6de35a5beb8534e19579cf1058460b4eb2bc79)
% id = "01HP1FESY3RW98YQZFS04Q7E7C"
- Perforce changelist 31126352
% id = "01HP1FESY3XJRMHKB173XWF130"
+ the API revolves around the `UE::DataValidation::IFixer` type. _fixers_ are pieces of code that can fix your issues given their application conditions are met
% id = "01HP1FESY38JWXNSJKPZNHP33Q"
+ the interface you have to implement is:
```cpp
struct IFixer
{
virtual EFixApplicability GetApplicability(int32 FixIndex) const = 0;
virtual FFixResult ApplyFix(int32 FixIndex) = 0;
}
```
% id = "01HP1FESY3PZH00WDD85CNF1BX"
- for the curious cats among us: `FixIndex` is an arbitrary number that can be passed to `FFixToken`, to handle multiple different fixes with one fixer
% id = "01HP1FESY3328D7ECZZX3EZCNE"
+ first, the editor calls `GetApplicability` to know whether the fix can be applied
% id = "01HP1FESY32EFSRX75ZWJ0K7PW"
- `EFixApplicability::CanBeApplied` should be returned if the fix can be applied at the moment
% id = "01HP1FESY3RN3ECQHJ8R3HS4GD"
- `EFixApplicability::Applied` should be returned if the fix has been applied and can no longer be applied anymore
% id = "01HP1FESY327FZMRZ64844XC9A"
- `EFixApplicability::DidNotApply` should be returned if the fix was not applied and can no longer be applied anymore
% id = "01HP1FESY3N19T00CNY2T182VG"
+ then, if `GetApplicability()` returns `EFixApplicability::CanBeApplied`, `ApplyFix` is called to actually run the fix
% id = "01HP1FESY3NGQRKA57QTTVC19W"
- this function is safe to assume the caller has ensured that the fix `CanBeApplied` and therefore need not do any extra validity checks
% id = "01HP1FESY3AAEHF4HSMAJ3B21H"
- other than that it's free to do whatever it wants
% id = "01HP1FESY3CBMBJA3CQPEMCFND"
- after a fix is applied, the fixer returns an `FFixResult` which is used to display a notification to the user informing them of the changes
% id = "01HP1FESY3GB4N76JDV8RVG4VD"
- fortunately you don't have to define a new struct for each fix, and instead can use `UE::DataValidation::TLambdaFixer` together with the `UE::DataValidation::MakeFix` function for more convenience
% id = "01HP1FESY3EJ7TK4SPTD0TBBN9"
+ once you have a fixer, you can display it in your (`FTokenizedMessage`-based) validation messages using `FFixToken`
% id = "01HP1FESY30HDA6JNR323GXD3D"
- minimal example:
```cpp
auto Message = FTokenizedMessage::Create(
EMessageVerbosity::Error,
LOCTEXT(
"FoundNoneEntries",
"Found None entries in ability array."
"Please remove them, otherwise they may cause crashes during runtime."
)
);
Message->AddToken(
UE::DataValidation::MakeFix(
[this]
{
Abilities.SetNum(Algo::RemoveIf(
Abilities,
[](const UGameplayAbility* Ability) { return Ability == nullptr; }
));
return FFixResult::Success(LOCTEXT("NoneEntriesRemoved", "None entries have been removed"));
}
)
->CreateToken(LOCTEXT("RemoveNoneEntries", "Remove the None entries"))
);
Context.AddMessage(MoveTemp(Message));
```
% id = "01HP1FESY3V73R3JZEQHQ36V58"
- the user can then apply a fix by clicking on it in the Message Log message that contains it
% id = "01HP1FESY3JM8WPZMRNCZC8GBA"
+ fixers can be freely stacked and composed - there are a few such layers available out of the box in the engine
% id = "01HP1FESY3VK9X1TB81VR2VA83"
- the set is quite limited at the moment, but you're free to create your own or contribute them to mainline Unreal :ralsei_love:
% id = "01HP1FESY3TC6DMBWQ3CEMGRW1"
- `UE::DataValidation::FSingleUseFixer` makes it so that your fix's applicability becomes `EFixApplicability::Applied` after the user applies the fix
% id = "01HP1FESY32ZA4K57ATCKPM800"
- `UE::DataValidation::FObjectSetDependentFixer` makes it so that your fix becomes `DidNotApply` after the specified objects are deleted from memory
% id = "01HP1FESY3W381HMBCEGCZ3HK7"
- `UE::DataValidation::FAutoSavingFixer` tells the user to save any assets modified by the fix after it's applied
% id = "01HP1FESY3SENV59V4462YSZJY"
- `UE::DataValidation::FValidatingFixer` runs data validation on any assets modified by the fix after it's applied
% id = "01HP1FESY3AZN3AVF7Z02JHXZA"
- `UE::DataValidation::FMutuallyExclusiveFixSet` is actually not a fixer, but a fixer _builder_ -
you give it a set of fixers, and it will make it so that when one is applied, it becomes `EFixApplicability::Applied`, and the rest becomes `EFixApplicability::DidNotApply` -
thus creating a set of mutually-exclusive fixes
% id = "01HP1FESY3CC1NZZMQQP56TTD4"
- you can refer to the `DataValidationFixers.h` header file for more documentation
% id = "01HP1FESY3WH21KDDYAHWZG294"
- all of this is going to be available in Unreal 5.4 :sparkles: