New generic attribute set codegen from table implemented

This commit is contained in:
Edward R. Gonzalez 2024-10-22 13:07:50 -04:00
parent d4bf7cfaec
commit 8ba9170794
11 changed files with 499 additions and 40 deletions

View File

@ -5,7 +5,7 @@ ProjectName=GASATHON
CopyrightNotice=
[/Script/Gasa.GasaDevOptions]
+AttributeSets=/Game/Core/Tables/DT_AttributeSet.DT_AttributeSet
+AttributeSets=/Game/Core/Tables/DT_GasaAttributeSet.DT_GasaAttributeSet
TaggedMessageTable=/Game/Core/Tables/DT_TaggedMessages.DT_TaggedMessages
Template_PlayerCamera=/Game/Actors/BP_CameraMount.BP_CameraMount_C
Template_HUD_HostUI=/Game/UI/UI_Host.UI_Host_C

View File

@ -0,0 +1,10 @@
{
"ColumnWidths":
{
"MaxValue": 165,
"Description": 135,
"Category": 96,
"BaseValue": 106,
"Name": 82
}
}

View File

@ -13,8 +13,8 @@ struct GASA_API FAttributeSetField : public FTableRowBase
FAttributeSetField()
: Name("Provide_Name")
, Description("Provide Description")
, Category("Optional Category")
, Description("Provide Description")
, BaseValue(0)
, bUseMinAttribute(false)
, bUseMaxAttribute(false)
@ -27,14 +27,14 @@ struct GASA_API FAttributeSetField : public FTableRowBase
{}
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
FString Name;
FName Name;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
FName Category;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
FString Category;
// UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute")
// FGameplayTag Tag;
@ -59,4 +59,3 @@ struct GASA_API FAttributeSetField : public FTableRowBase
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Gameplay Attribute", meta =( EditCondition="bUseMaxAttribute==false", EditConditionHides))
float MaxValue;
};

View File

@ -16,9 +16,6 @@ public:
// NOTE(Ed): Any Soft-References must have their includes defined in GasaDevOptions.cpp
// They are used by GasaGen for the GasaDevOptionsCache
UPROPERTY(Config)
TSoftObjectPtr<UDataTable> RandomBullshit;
UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category="GAS")
TArray< TSoftObjectPtr<UDataTable>> AttributeSets;

View File

@ -11,9 +11,6 @@ void FGasaDevOptionsCache::CachedDevOptions()
{
UGasaDevOptions* DevOpts = GetMutDevOptions();
RandomBullshit = DevOpts->RandomBullshit.LoadSynchronous();
ensureMsgf( RandomBullshit != nullptr, TEXT( "RandomBullshit is null, DO NOT RUN PIE or else you may get a crash if not handled in BP or C++" ) );
for ( auto& entry : DevOpts->AttributeSets )
{
AttributeSets.Push( entry.LoadSynchronous() );

View File

@ -8,8 +8,6 @@ struct GASA_API FGasaDevOptionsCache
{
GENERATED_BODY()
UPROPERTY()
UObject* RandomBullshit;
UPROPERTY()
TArray<UObject*> AttributeSets;
UPROPERTY()

View File

@ -1,5 +1,6 @@
#include "GasaGen.h"
#include "GasaGen_Common.h"
#include "GasaGen_AttributeSets.h"
#include "GasaGen_DevOptionsCache.h"
// Editor Module
@ -19,9 +20,9 @@ global Code UModule_GASA_API;
void Execute_GasaModule_Codegen()
{
FScopedSlowTask SlowTask(100.0f, LOCTEXT("RunningGasaGen", "Running GasaGen..."));
SlowTask.MakeDialog(); // Shows a progress dialog
SlowTask.MakeDialog(false, true); // Shows a progress dialog
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [&SlowTask]()
AsyncTask(ENamedThreads::AnyBackgroundHiPriTask, [&SlowTask]()
{
Gasa::LogEditor("Executing: Gasa Module code generation.");
@ -36,9 +37,7 @@ void Execute_GasaModule_Codegen()
char const* ue_ansi_rooot_path = TCHAR_TO_ANSI(*ue_project_path);
Project_Path = String::make_length(GlobalAllocator, ue_ansi_project_path, ue_project_path.Len());
Root_Path = String::make_length(GlobalAllocator, ue_ansi_rooot_path, ue_root_path.Len());
UE_LOG(LogTemp, Log, TEXT("Current ROOT Directory: %s"), *ue_project_path);
UE_LOG(LogTemp, Log, TEXT("Current Project Directory: %s"), *ue_root_path);
Root_Path = String::make_length(GlobalAllocator, ue_ansi_rooot_path, ue_root_path.Len());
// Initialize Globals
{
@ -112,16 +111,11 @@ void Execute_GasaModule_Codegen()
PreprocessorDefines.append(get_cached_string(str_UE_REQUIRES));
}
//generate_AttributeSets();
generate_DevOptionsCache();
generate_AttributeSets();
//generate_DevOptionsCache();
//generate_HostWidgetController();
gen::deinit();
AsyncTask(ENamedThreads::GameThread, []()
{
// UI updates if needed
});
});
}

View File

@ -1,8 +1,463 @@
#include "GasaGen_AttributeSets.h"
#include "GasaGen_Common.h"
#include "Gasa/AbilitySystem/GasaAbilitySystem.h"
#include "Gasa/GasaDevOptions.h"
using namespace gen;
#pragma push_macro("UPROPERTY")
#pragma push_macro("UFUNCTION")
#pragma push_macro("FORCEINLINE")
#pragma push_macro("ensure")
#undef UPROPERTY
#undef UFUNCTION
#undef FORCEINLINE
#undef ensure
PRAGMA_DISABLE_OPTIMIZATION
void generate_AttributeSets()
{
// All attribute sets are tracked in Gasa's dev options for this project.
TArray< TSoftObjectPtr<UDataTable>> AttributeSetTables = Gasa::GetDevOptions()->AttributeSets;
check( AttributeSetTables.Num() > 0 );
// TODO(Ed): Doing one for now
FGraphEventRef LoadTableTask;
UDataTable* AttributeSetTable = nullptr;
FGraphEventArray Prerequisites;
LoadTableTask = FFunctionGraphTask::CreateAndDispatchWhenReady(
[ & AttributeSetTable, & AttributeSetTables ]()
{
AttributeSetTable = AttributeSetTables[0].LoadSynchronous();
},
TStatId(), &Prerequisites, ENamedThreads::GameThread
);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(LoadTableTask);
TMap<FName, TArray<FAttributeSetField>> AttributesByCategory;
{
TMap< FName, uint8* > const& RowMap = AttributeSetTable->GetRowMap();
for (const TPair<FName, uint8*>& Row : RowMap)
{
FAttributeSetField const* RowData = rcast( FAttributeSetField const* , Row.Value);
// If category is empty, use a default category name
FName CategoryName = RowData->Category.IsNone() ? FName(TEXT("Default")) : RowData->Category;
TArray<FAttributeSetField>& CategoryAttributes = AttributesByCategory.FindOrAdd(CategoryName);
CategoryAttributes.Add( * RowData);
}
}
FString AssetName = AttributeSetTables[0].GetAssetName();
check( AssetName.StartsWith(TEXT("DT_") ))
AssetName = AssetName.RightChop(3);
String str_AssetName = to_string(AssetName);
String class_name = String::fmt_buf(GlobalAllocator, "U%S", str_AssetName);
String header_file_name = String::fmt_buf(GlobalAllocator, "%S.h", str_AssetName);
String inlines_file_name = String::fmt_buf(GlobalAllocator, "%S_Inlines.h", str_AssetName);
String path_header_file = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S.h", str_AssetName);
String path_inlines_file = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S_Inlines.h", str_AssetName);
String path_source_file = String::fmt_buf(GlobalAllocator, path_gasa_ability_system "%S.cpp", str_AssetName);
String uht_include_file = String::fmt_buf(GlobalAllocator, "%S.generated.h", str_AssetName);
CodeType type_UAttributeSet = def_type( txt("UAttributeSet") );
CodeComment generation_notice = def_comment(txt("Generated by GasaEditor/GasaGen/GasaGen_AttributeSets.cpp"));
CodeAttributes api_attribute = def_attributes( UModule_GASA_API->Name);
Builder header = builder_open( path_header_file );
{
header.print(generation_notice);
header.print(pragma_once);
header.print(fmt_newline);
CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h"));
CodeInclude Include_GasaAttributeSet_Generated = def_include(uht_include_file);
header.print( Include_AttributeSet);
header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline);
gen::CodeClass attribute_set_class = {};
{
CodeBody body = def_body( CodeT::Class_Body );
{
body.append( UHT_GENERATED_BODY);
body.append( access_public );
body.append( def_constructor() );
body.append(fmt_newline);
// Generate UPROPERTIES for each attribute field, organized by category
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
{
String category_name = to_string(attributes.Key);
CodeComment category_cmt = def_comment(token_fmt("category_name", (StrC)category_name, "<category_name> Attributes"));
body.append(category_cmt);
body.append(fmt_newline);
for (FAttributeSetField attribute : attributes.Value)
{
Code field_uproperty = code_fmt(
"category", (StrC)to_string(attribute.Category),
"property", (StrC)to_string(attribute.Name),
stringize(
UPROPERTY(ReplicatedUsing = Client_OnRep_<property>, EditAnywhere, BlueprintReadWrite, Category = "Attributes|<category>")
FGameplayAttributeData <property>;
));
body.append(field_uproperty);
}
body.append(fmt_newline);
body.append(fmt_newline);
}
// Generate OnReps for each attribute field
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
for ( FAttributeSetField attribute : attributes.Value )
{
Code umeta_UFUNCTION = code_str(UFUNCTION());
body.append(fmt_newline);
body.append(umeta_UFUNCTION);
body.append(fmt_newline);
body.append(code_fmt("property", (StrC)to_string(attribute.Name), stringize(
void Client_OnRep_<property>(FGameplayAttributeData& Prev<property>);
)));
}
body.append(fmt_newline);
body.append(fmt_newline);
body.append(def_pragma(txt("region Getters")));
{
// Generate property getters
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
for (FAttributeSetField attribute : attributes.Value)
{
CodeFn generated_get_attribute = parse_function(token_fmt(
"class_name", (StrC)class_name,
"property", (StrC)to_string(attribute.Name),
stringize(
static FGameplayAttribute Get<property>Attribute()
{
static FProperty* Prop = FindFieldChecked<FProperty>(<class_name>::StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
return Prop;
}
)));
body.append(generated_get_attribute);
}
body.append(fmt_newline);
// Generate value getters
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
for (FAttributeSetField attribute : attributes.Value)
{
body.append(code_fmt("property", (StrC)to_string(attribute.Name),
stringize(
FORCEINLINE float Get<property>() const
{
return <property>.GetCurrentValue();
}
)));
}
body.append(fmt_newline);
}
body.append(def_pragma(txt("endregion Getters")));
body.append(fmt_newline);
body.append(def_pragma(txt("region Setters")));
{
// Generate value setter forwards
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
for (FAttributeSetField attribute : attributes.Value)
{
body.append(code_fmt("property", (StrC)to_string(attribute.Name),
stringize(
FORCEINLINE void Set<property>(float NewVal);
)));
}
body.append(fmt_newline);
body.append(fmt_newline);
// Generate initers
for ( TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory )
for (FAttributeSetField attribute : attributes.Value)
{
body.append(code_fmt("property", (StrC)to_string(attribute.Name),
stringize(
FORCEINLINE void Init<property>(float NewVal)
{
<property>.SetBaseValue(NewVal);
<property>.SetCurrentValue(NewVal);
}
)));
}
}
body.append(def_pragma(txt("endregion Setters")));
body.append(fmt_newline);
body.append(def_pragma(txt("region AttributeSet")));
body.append(code_str(
void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
void PostGameplayEffectExecute(FGameplayEffectModCallbackData const& Data) override;
));
body.append(def_pragma(txt("endregion AttributeSet")));
body.append(fmt_newline);
body.append(def_pragma(txt("region UObject")));
CodeFn GetLifetimeOfReplicatedProps = parse_function(code(
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>&OutLifetimeProps) const override;
));
body.append(GetLifetimeOfReplicatedProps);
body.append(def_pragma(txt("endregion UObject")));
body.append(fmt_newline);
}
attribute_set_class = def_class( class_name, body
, type_UAttributeSet, AccessSpec::Public
, api_attribute
);
header.print(UHT_UCLASS);
header.print(attribute_set_class);
}
header.write();
format_file(path_header_file);
}
Builder inlines = builder_open( path_inlines_file );
{
inlines.print(generation_notice);
inlines.print(pragma_once);
inlines.print(fmt_newline);
inlines.print(def_include(header_file_name));
inlines.print(def_include(txt("AbilitySystemComponent.h")));
inlines.print(fmt_newline);
CodeBody body = def_body(ECode::Global_Body);
{
body.append(def_pragma(txt("region Attribute Setters")));
{
for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
for (FAttributeSetField attribute : attributes.Value)
{
CodeFn generated_get_attribute = parse_function(token_fmt(
"class_name", (StrC)class_name,
"property", (StrC)to_string(attribute.Name),
stringize(
FORCEINLINE void <class_name>::Set<property>(float NewVal)
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
if (ensure(AbilityComp))
{
AbilityComp->SetNumericAttributeBase(Get<property>Attribute(), NewVal);
};
}
)));
body.append(generated_get_attribute);
}
}
body.append(def_pragma(txt("endregion Attribute Setters")));
}
inlines.print(body);
inlines.print(fmt_newline);
CodeNS ns_gasa = parse_namespace(token_fmt(
"class_name", (StrC)class_name,
stringize(
namespace Gasa
{
inline
<class_name> const* GetAttributeSet(UAbilitySystemComponent* ASC)
{
return Cast< <class_name> >(ASC->GetAttributeSet(<class_name>::StaticClass()));
}
}
)));
inlines.print(ns_gasa);
inlines.write();
format_file(path_inlines_file);
}
Builder source = builder_open( path_source_file );
{
source.print(generation_notice);
header.print(fmt_newline);
source.print(def_include(header_file_name));
source.print(def_include(inlines_file_name));
source.print(def_include(txt("EffectProperties.h")));
source.print(fmt_newline);
source.print(def_include(txt("AbilitySystemComponent.h")));
source.print(def_include(txt("Net/UnrealNetwork.h")));
source.print(def_include(txt("Networking/GasaNetLibrary.h")));
source.print(def_include(txt("GameplayEffectExtension.h")));
{
CodeBody body = def_body(CodeT::Global_Body);
body.append(fmt_newline);
CodeConstructor constructor_for_UGasaAttributeSet = parse_constructor(code(
UGasaAttributeSet::UGasaAttributeSet()
{}
));
body.append(constructor_for_UGasaAttributeSet);
// Generate Attribute fields implementation
{
body.append(fmt_newline);
body.append(def_pragma(txt("region Rep Notifies")));
for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
for (FAttributeSetField attribute : attributes.Value)
{
CodeFn field_impl = parse_function(token_fmt(
"class_name", (StrC)class_name,
"property", (StrC)to_string(attribute.Name),
"from_notice", txt("\n// From GAMEPLAYATTRIBUTE_REPNOTIFY\n"),
stringize(
void <class_name>::Client_OnRep_<property>(FGameplayAttributeData & Prev<property>)
{
<from_notice>
static FProperty* <class_name>Property = FindFieldChecked<FProperty>(StaticClass(), GET_MEMBER_NAME_CHECKED(<class_name>, <property>));
GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(<class_name>Property), <property>, Prev<property>);
}
)));
body.append(field_impl);
}
body.append(def_pragma(txt("endregion Rep Notifies")));
body.append(fmt_newline);
}
CodeFn PostGameplayEffectExecute;
CodeFn PreAttributeChange;
{
CodeBody pre_attribute_clamps = def_body(CodeT::Function_Body);
CodeBody post_attribute_clamps = def_body(CodeT::Function_Body);
// Generate field clamping ops for the pre & post functions
{
pre_attribute_clamps.append(fmt_newline);
pre_attribute_clamps.append(fmt_newline);
post_attribute_clamps.append(fmt_newline);
post_attribute_clamps.append(fmt_newline);
for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
for (FAttributeSetField attribute : attributes.Value)
{
String clamp_min;
if (attribute.bUseMinAttribute)
clamp_min = get_cached_string(token_fmt("MinName", (StrC)to_string(attribute.MinAttribute), "Get<MinName>()"));
else
clamp_min = String::fmt_buf(GlobalAllocator, "%f", attribute.MinValue);
String clamp_max;
if (attribute.bUseMaxAttribute)
clamp_max = get_cached_string(token_fmt("MaxName", (StrC)to_string(attribute.MaxAttribute), "Get<MaxName>()"));
else
clamp_max = String::fmt_buf(GlobalAllocator, "%f", attribute.MaxValue);
pre_attribute_clamps.append(code_fmt(
"field", (StrC)to_string(attribute.Name),
"clamp_min", (StrC)clamp_min,
"clamp_max", (StrC)clamp_max,
stringize(
if (Attribute == Get<field>Attribute())
{
NewValue = FMath::Clamp(NewValue, <clamp_min>, <clamp_max>);
}
)));
post_attribute_clamps.append(code_fmt(
"field", (StrC)to_string(attribute.Name),
"clamp_min", (StrC)clamp_min,
"clamp_max", (StrC)clamp_max,
stringize(
if (Data.EvaluatedData.Attribute == Get<field>Attribute())
{
Set<field>(FMath::Clamp(Get<field>(), <clamp_min>, <clamp_max>));
}
)));
}
pre_attribute_clamps.append(fmt_newline);
pre_attribute_clamps.append(fmt_newline);
post_attribute_clamps.append(fmt_newline);
post_attribute_clamps.append(fmt_newline);
}
PreAttributeChange = parse_function(token_fmt(
"class_name", (StrC)class_name,
"attribute_clamps", (StrC)pre_attribute_clamps.to_string(),
stringize(
void <class_name>::PreAttributeChange(FGameplayAttribute const& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
<attribute_clamps>
}
)));
PostGameplayEffectExecute = parse_function(token_fmt(
"class_name", (StrC)class_name,
"attribute_clamps", (StrC)post_attribute_clamps.to_string(),
stringize(
void <class_name>::PostGameplayEffectExecute(FGameplayEffectModCallbackData const& Data)
{
Super::PostGameplayEffectExecute(Data);
FEffectProperties Props;
Props.Populate(Data);
<attribute_clamps>
}
)));
body.append(PostGameplayEffectExecute);
body.append(fmt_newline);
body.append(PreAttributeChange);
body.append(fmt_newline);
}
CodeFn GetLifetimeOfReplicatedProps;
{
CodeBody field_lifetimes = def_body(CodeT::Function_Body);
field_lifetimes.append(fmt_newline);
field_lifetimes.append(fmt_newline);
for (TPair< FName, TArray<FAttributeSetField>>& attributes : AttributesByCategory)
for (FAttributeSetField attribute : attributes.Value)
{
field_lifetimes.append(code_fmt(
"class_name", (StrC)class_name,
"property", (StrC)to_string(attribute.Name),
stringize(
DOREPLIFETIME_DEFAULT_GAS( <class_name>, <property> );
)));
}
GetLifetimeOfReplicatedProps = parse_function(token_fmt(
"class_name", (StrC)class_name,
"property_lifetimes", (StrC)(field_lifetimes.to_string()),
stringize(
void <class_name>::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
<property_lifetimes>
}
)));
body.append(GetLifetimeOfReplicatedProps);
}
source.print(body);
}
source.write();
format_file(path_source_file);
}
}
PRAGMA_ENABLE_OPTIMIZATION
#pragma pop_macro("UPROPERTY")
#pragma pop_macro("UFUNCTION")
#pragma pop_macro("FORCEINLINE")
#pragma pop_macro("ensure")

View File

@ -1,3 +1,6 @@
#pragma once
void generate_attribute_sets();
void generate_AttributeSets();

View File

@ -4,8 +4,6 @@
#include "gencpp/gen.builder.hpp"
using namespace gen;
#undef check
// Codegen assumes its working directory is the project
#define path_scripts "/scripts/"
#define path_project "/Project/"
@ -121,11 +119,20 @@ void format_file( char const* path )
log_fmt("\tRunning clang-format on file:\n");
system( command );
log_fmt("\tclang-format finished reformatting.\n");
FString command_fstr = FString( command.Data, command.length());
UE_LOG(LogTemp, Log, TEXT("clang format command: %s"), *command_fstr );
#undef cf_cmd
#undef cf_format_inplace
#undef cf_style
#undef cf_verbse
}
FORCEINLINE
String to_string( FString ue_string ) {
char const* ansi_str = TCHAR_TO_ANSI(*ue_string);
return String::make_length(GlobalAllocator, ansi_str, ue_string.Len());
}
FORCEINLINE
String to_string( FName ue_fname ) {
char const* ansi_str = TCHAR_TO_ANSI(*ue_fname.ToString());
return String::make_length(GlobalAllocator, ansi_str, ue_fname.GetStringLength());
}

View File

@ -105,9 +105,9 @@ void gen_UGasaAttributeSet()
Array<GAS_AttributeEntry> primary_attribute_fields = get_gasa_primary_attribute_fields();
Array<GAS_AttributeEntry> secondary_attribute_fields = get_gasa_secondary_attribute_fields();
Array<GAS_AttributeEntry> vital_attribute_fields = get_gasa_vital_attribute_fields();
s32 all_attrib_count = primary_attribute_fields.num() + secondary_attribute_fields.num() + vital_attribute_fields.num();
Array< GAS_AttributeEntry>
all_attribute_fields = Array<GAS_AttributeEntry>::init_reserve(GlobalAllocator, all_attrib_count);
all_attribute_fields.append( primary_attribute_fields);
@ -124,6 +124,9 @@ void gen_UGasaAttributeSet()
{
CodeInclude Include_AttributeSet = def_include(txt("AttributeSet.h"));
CodeInclude Include_GasaAttributeSet_Generated = def_include(txt("GasaAttributeSet.generated.h"));
header.print( Include_AttributeSet);
header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline);
CodeAttributes api_attribute = def_attributes( UModule_GASA_API->Name);
@ -144,8 +147,7 @@ void gen_UGasaAttributeSet()
body.append(fmt_newline);
// body.append( def_comment(txt("Secondary Attribute Fields")));
// body.append(fmt_newline);
// def_attribute_properties( body, secondary_attribute_fields);
// body.append(fmt_newline);sssss// def_attribute_properties( body, secondary_attribute_fields);
// body.append(fmt_newline);
// body.append(fmt_newline);
@ -205,9 +207,6 @@ void gen_UGasaAttributeSet()
);
}
header.print( Include_AttributeSet);
header.print( Include_GasaAttributeSet_Generated);
header.print( fmt_newline);
header.print( UHT_UCLASS );
header.print(GasaAttributeSet);
}