Extending UHT for custom Quest System purposes

Our Quest System (QS) is a separate C# tool. The part I’m currently extending is support for what we internally call triggers — these are essentially Blueprint events.

In the QS, each graph (conceptually similar to a Blueprint graph) is generated into C++ code and compiled into a plugin DLL. A trigger in this context is simply an event entry point. The goal is that when a C++ programmer wants to bind custom logic to one of these events, they can mark the function with a custom specifier, similar to BlueprintNativeEvent:

UFUNCTION(QuestSystemNativeEvent)
void OnInteract();

This would generate the function definition in the same pattern as BlueprintNativeEvent.

So if someone wants custom C++ logic, they would implement it as:

void USomeClass::OnInteract_Implementation()
{
    ...
}

The quest system generated code will run inside the generated USomeClass::OnInteract() method, and afterwards OnInteract_Implementation() will be called if it exists.

We’re also considering optionally allowing the user to specify whether the custom C++ implementation runs before or after the generated trigger code — but that’s a detail we can solve later.

My plan is to extend UHT with the new specifier and implement generation of the method definitions as described above.

Additionally, we would like UHT to collect all UFUNCTIONs that use QuestSystemNativeEvent, so that the C# QS side can automatically generate the proper node definitions for these triggers.

Mainly writing this to make sure you’re aware of the change — if you have any objections or concerns about implementing this direction, let me know.

[Attachment Removed]

Hi Petr,

That sounds like a good extension to UHT. I would recommend that you make sure to add comments\markers around the places that you are modifying. This should help when you integrate newer release of the engine.

Regards,

Martin

[Attachment Removed]

Hi Martin,

thank you for your answer, so far it looks good, but we have modified the Enum EFunctionFlags, which has only two unused bits

0x00000010 and 0x00000020, I used one of them to set the SkaldEvent = 0x00000010, my concern is that you might want to reuse these in the future, instead of extending the enum to 64bits, when we talked to each other you said you might asked the responsible team, so in case it is not safe to use it, we can maybe come up with some workaround.

Kind regards,

Petr

[Attachment Removed]

Hey Petr, Martin looped me in. Have you looked at using function metadata instead? You can define arbitrary names and key-value pairs in UFUNCTION() declarations as meta data:

UFUNCTION(meta=(QuestFunction))
void MyQuestFunction() {}
 
UFUNCTION()
void MyRegularFunction() {}

You’re building an editor system, and function metadata is available editor-time:

UFunction* Func = FindFunctionChecked(FName("MyQuestFunction"));
UE_LOG(LogTemp, Warning, TEXT("Is MyQuestFunction a quest function? %d"), int32(Func->HasMetaData(FName("QuestFunction"))));
UFunction* Func2 = FindFunctionChecked(FName("MyRegularFunction"));
UE_LOG(LogTemp, Warning, TEXT("Is MyRegularFunction a quest function? %d"), int32(Func2->HasMetaData(FName("QuestFunction"))));

As a better practice, you would define metadata FNames somewhere as a const. For UE’s own stuff that’s in FBlueprintMetaData::MD_.

As for letting blueprint programmers mark their functions as quest functions, you can extend the editor so that the details panel for functions has a checkbox to set metadata on the function. MD_UnsafeForConstructionScripts in engine is a good example of that.

Would that meet your requirements?

[Attachment Removed]

Since metadata can be arbitrary names, you don’t run the risk of the enum flag being used by engine code in the future.

[Attachment Removed]

We had company wide vacation, so sorry for late response, but your suggestion looks safer, we will try to rewrite it.

[Attachment Removed]

Great, I would love to hear whether this suggestion works out.

[Attachment Removed]