diff --git a/Mods/SML/Source/SML/Private/Module/GameWorldModule.cpp b/Mods/SML/Source/SML/Private/Module/GameWorldModule.cpp index 33b39ec99a..964de31128 100644 --- a/Mods/SML/Source/SML/Private/Module/GameWorldModule.cpp +++ b/Mods/SML/Source/SML/Private/Module/GameWorldModule.cpp @@ -1,6 +1,7 @@ #include "Module/GameWorldModule.h" #include "Command/ChatCommandLibrary.h" #include "Registry/ModContentRegistry.h" +#include "Registry/ContentTagRegistry.h" #include "Subsystem/SubsystemActorManager.h" #if WITH_EDITOR @@ -53,7 +54,9 @@ void UGameWorldModule::RegisterDefaultContent() { UWorld* WorldObject = GetWorld(); UModContentRegistry* ModContentRegistry = UModContentRegistry::Get(WorldObject); AChatCommandSubsystem* ChatCommandSubsystem = AChatCommandSubsystem::Get(WorldObject); + UContentTagRegistry* ContentTagRegistry = UContentTagRegistry::Get(WorldObject); check(ModContentRegistry); + check(ContentTagRegistry); auto ModReference = GetOwnerModReference(); @@ -77,6 +80,12 @@ void UGameWorldModule::RegisterDefaultContent() { ModContentRegistry->RegisterResourceSinkItemPointTable(ModReference, ModExplorationResourceSinkPointsTable, EResourceSinkTrack::RST_Exploration); } + //Register tag additions + UDataTable* ContentTagAdditionsTable = mContentTagAdditionsTable.LoadSynchronous(); + if (ContentTagAdditionsTable != NULL) { + ContentTagRegistry->RegisterTagAdditionTable(ModReference, ContentTagAdditionsTable); + } + //Register chat commands (on server side only) if (ChatCommandSubsystem != NULL) { for (const TSubclassOf& ChatCommand : mChatCommands) { diff --git a/Mods/SML/Source/SML/Private/Registry/ContentTagRegistry.cpp b/Mods/SML/Source/SML/Private/Registry/ContentTagRegistry.cpp index 29745d8046..b03aa9d257 100644 --- a/Mods/SML/Source/SML/Private/Registry/ContentTagRegistry.cpp +++ b/Mods/SML/Source/SML/Private/Registry/ContentTagRegistry.cpp @@ -5,8 +5,6 @@ DEFINE_LOG_CATEGORY(LogContentTagRegistry); -FFrame* UContentTagRegistry::ActiveScriptFramePtr{}; - /** Makes sure provided object instance is valid, crashes with both script call stack and native stack trace if it's not */ #define NOTIFY_INVALID_REGISTRATION(Context) \ { \ @@ -113,22 +111,14 @@ void UContentTagRegistry::RegisterTagAdditionTable(FName ModReference, UDataTabl return; } - if (!IsValid(PointTable->RowStruct) || !PointTable->RowStruct->IsChildOf(FResourceSinkPointsData::StaticStruct())) { - const FString Context = FString::Printf(TEXT("Invalid AWESOME Sink item points table in mod %s (%s): Row Type should be Resource Sink Points Data"), - *ModReference.ToString(), *PointTable->GetPathName()); + if (!IsValid(TagTable->RowStruct) || !TagTable->RowStruct->IsChildOf(FContentTagRegistryAddition::StaticStruct())) { + const FString Context = FString::Printf(TEXT("Invalid Content Tag Addition table in mod %s (%s): Row Type should be FContentTagRegistryAddition"), + *ModReference.ToString(), *TagTable->GetPathName()); NOTIFY_INVALID_REGISTRATION(*Context); return; } - FPendingResourceSinkRegistration Registration; - Registration.Track = Track; - Registration.ModReference = ModReference; - Registration.PointTable = PointTable; - - PendingItemSinkPointsRegistrations.Add(Registration); - if (AFGResourceSinkSubsystem* ResourceSinkSubsystem = AFGResourceSinkSubsystem::Get(GetWorld())) { - FlushPendingResourceSinkRegistrations(ResourceSinkSubsystem); - } + UE_LOG(LogContentTagRegistry, Verbose, TEXT("TODO Registering tag addition table '%s' from mod %s"), *TagTable->GetPathName(), *ModReference.ToString()); } void UContentTagRegistry::FreezeRegistry() { @@ -177,3 +167,35 @@ FString UContentTagRegistry::GetCallStackContext() { } return FString::Printf(TEXT("%hs:%d"), NativeStackTrace[FirstExternalFrameIndex].Filename, NativeStackTrace[FirstExternalFrameIndex].LineNumber); } + +FFrame* UContentTagRegistry::ActiveScriptFramePtr{}; + +DEFINE_FUNCTION(UContentTagRegistry::execAddGameplayTagsTo) { + P_GET_OBJECT(UClass, Z_Param_Content); + P_GET_STRUCT(FGameplayTagContainer, Z_Param_TagContainer) + P_FINISH; + P_NATIVE_BEGIN; + TGuardValue ScopedFrameOverride(ActiveScriptFramePtr, &Stack); + P_THIS->AddGameplayTagsTo(Z_Param_Content, Z_Param_TagContainer); + P_NATIVE_END; +} + +DEFINE_FUNCTION(UContentTagRegistry::execRemoveGameplayTagsFrom) { + P_GET_OBJECT(UClass, Z_Param_Content); + P_GET_STRUCT(FGameplayTagContainer, Z_Param_TagContainer) + P_FINISH; + P_NATIVE_BEGIN; + TGuardValue ScopedFrameOverride(ActiveScriptFramePtr, &Stack); + P_THIS->RemoveGameplayTagsFrom(Z_Param_Content, Z_Param_TagContainer); + P_NATIVE_END; +} + +DEFINE_FUNCTION(UContentTagRegistry::execRegisterTagAdditionTable) { + P_GET_PROPERTY(FNameProperty, ModReference); + P_GET_OBJECT(UDataTable, PointTable); + P_FINISH; + P_NATIVE_BEGIN; + TGuardValue ScopedFrameOverride(ActiveScriptFramePtr, &Stack); + P_THIS->RegisterTagAdditionTable(ModReference, PointTable); + P_NATIVE_END; +} diff --git a/Mods/SML/Source/SML/Public/Module/GameWorldModule.h b/Mods/SML/Source/SML/Public/Module/GameWorldModule.h index 9a1723dee1..20e5c2a4b2 100644 --- a/Mods/SML/Source/SML/Public/Module/GameWorldModule.h +++ b/Mods/SML/Source/SML/Public/Module/GameWorldModule.h @@ -50,6 +50,14 @@ class SML_API UGameWorldModule : public UWorldModule { UPROPERTY(EditDefaultsOnly, Category = Advanced) TSoftObjectPtr mExplorationResourceSinkItemPointsTable; + /** + * Table to use for Content Tag Registry additions. + * Useful for adding tags to content you don't control, for example, + * items from the base game and other mods. + */ + UPROPERTY(EditDefaultsOnly, Category = Advanced) + TSoftObjectPtr mContentTagAdditionsTable; + /** Mod subsystem actors to be registered automatically during construction phase */ UPROPERTY(EditDefaultsOnly, Category= Advanced) TArray> ModSubsystems; diff --git a/Mods/SML/Source/SML/Public/Registry/ContentTagRegistry.h b/Mods/SML/Source/SML/Public/Registry/ContentTagRegistry.h index 031d30685f..4a4e39ca6c 100644 --- a/Mods/SML/Source/SML/Public/Registry/ContentTagRegistry.h +++ b/Mods/SML/Source/SML/Public/Registry/ContentTagRegistry.h @@ -7,6 +7,25 @@ DECLARE_LOG_CATEGORY_EXTERN(LogContentTagRegistry, All, All); // TODO set arg2 to Log once done with feature +/** + * Row struct for the data table tag assignment registration approach + */ +USTRUCT(BlueprintType) +struct SML_API FContentTagRegistryAddition : public FTableRowBase { + GENERATED_BODY() + + FContentTagRegistryAddition() : + ItemClass(nullptr), + TagContainer(FGameplayTagContainer::EmptyContainer) + {} + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf ItemClass; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FGameplayTagContainer TagContainer; +}; + /** * Manages Gameplay Tag Containers for content classes. * Enables any mod to apply Unreal Gameplay Tags to any mod's content, @@ -43,16 +62,19 @@ class SML_API UContentTagRegistry : public UWorldSubsystem * Register gameplay tags from the passed container to the passed class * TODO do we want arg FName InRegistrationPluginName? */ - UFUNCTION(BlueprintCallable, Category = "Content Tag Registry") + UFUNCTION(BlueprintCallable, Category = "Content Tag Registry", CustomThunk) void AddGameplayTagsTo(UClass* content, const FGameplayTagContainer tags); /** * Remove gameplay tags in passed container from the passed class if they were present * TODO do we want arg FName InRegistrationPluginName? */ - UFUNCTION(BlueprintCallable, Category = "Content Tag Registry") + UFUNCTION(BlueprintCallable, Category = "Content Tag Registry", CustomThunk) void RemoveGameplayTagsFrom(UClass* content, const FGameplayTagContainer tags); + UFUNCTION(BlueprintCallable, Category = "Content Tag Registry", CustomThunk) + void RegisterTagAdditionTable(FName ModReference, UDataTable* TagTable); + // Freezes the registry. No new registrations are accepted past this point. void FreezeRegistry(); @@ -91,4 +113,9 @@ class SML_API UContentTagRegistry : public UWorldSubsystem // Gets tags offered via SML Extended Attribute Provider FGameplayTagContainer GetTagsFromExtendedAttributeProvider(UClass* content); + //Custom Thunks for calling Register functions through Reflection, primary point of which + //is providing FFrame callstack context to the registration methods for debugging in seamless manner + DECLARE_FUNCTION(execAddGameplayTagsTo); + DECLARE_FUNCTION(execRemoveGameplayTagsFrom); + DECLARE_FUNCTION(execRegisterTagAdditionTable); };