void AAbilitySystemDebugHUD::DrawDebugAbilitySystemComponent(UAbilitySystemComponent *Component) { UWorld *World = GetWorld(); float GameWorldTime = World->GetTimeSeconds(); UFont* Font = GEngine->GetSmallFont(); FColor Color(38, 128, 0); float X = 20.f; float Y = 20.f; FString String = FString::Printf(TEXT("%.2f"), Component->GetWorld()->GetTimeSeconds()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s (%d)"), *Component->GetPathName(), Component->IsDefaultSubobject()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s == %s"), *Component->GetArchetype()->GetPathName(), *Component->GetClass()->GetDefaultObject()->GetPathName()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); for (const UAttributeSet* Set : Component->SpawnedAttributes) { if (!Set) continue; check(Set); // Draw Attribute Set DrawWithBackground(Font, FString::Printf(TEXT("%s (%d)"), *Set->GetName(), Set->IsDefaultSubobject()), Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); String = FString::Printf(TEXT("%s == %s"), *Set->GetArchetype()->GetPathName(), *Set->GetClass()->GetDefaultObject()->GetPathName()); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); for (TFieldIterator<UProperty> PropertyIt(Set->GetClass(), EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty *Prop = *PropertyIt; FString ValueString; const void *PropertyValue = Prop->ContainerPtrToValuePtr<void>(Set); Prop->ExportTextItem(ValueString, PropertyValue, NULL, NULL, 0); String = FString::Printf(TEXT("%s: %s"), *Prop->GetName(), *ValueString); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); } Y+= 25; // Draw Active GameplayEffect for (FActiveGameplayEffect& Effect : &Component->ActiveGameplayEffects) { String = FString::Printf(TEXT("%s. [%d, %d] %.2f"), *Effect.Spec.ToSimpleString(), Effect.PredictionKey.Current, Effect.PredictionKey.Base, Effect.GetTimeRemaining(GameWorldTime)); DrawWithBackground(Font, String, Color, EAlignHorizontal::Left, X, EAlignVertical::Top, Y); } } }
void ULiveEditorKismetLibrary::ReplicateChangesToChildren( FName PropertyName, UObject *Archetype ) { if ( Archetype == NULL ) return; //find our child instances from the LiveEditManage lookup cache TArray< TWeakObjectPtr<UObject> > PiePartners; FLiveEditorManager::Get().FindPiePartners( Archetype, PiePartners ); for(TArray< TWeakObjectPtr<UObject> >::TIterator It(PiePartners); It; ++It) { if ( !(*It).IsValid() ) continue; UObject *Object = (*It).Get(); check( Object->IsA(Archetype->GetClass()) ); //little sanity check, but the object cache manages all this for us nLiveEditorKismetLibrary::CopyPropertyFromArchetype( Object, Archetype, PropertyName ); } void *ContainerPtr = Archetype; int32 ArrayIndex = 0; UProperty *Prop = nLiveEditorKismetLibrary::GetPropertyByName( Archetype, Archetype->GetClass(), PropertyName.ToString(), &ContainerPtr, ArrayIndex ); if ( Prop && Prop->IsA( UNumericProperty::StaticClass() ) ) { check( ContainerPtr != NULL ); FString ClassName = Archetype->GetClass()->GetName(); FString ValueString; void *Value = Prop->ContainerPtrToValuePtr<void>(ContainerPtr, ArrayIndex); Prop->ExportTextItem(ValueString, Value, NULL, NULL, 0); FLiveEditorManager::Get().BroadcastValueUpdate( ClassName, PropertyName.ToString(), ValueString ); } /** * Object iteration method should we want to dump the PIEObjectCache * Downside is that it is perceptably slow (though still usable) TArray<UObject*> ObjectsToChange; const bool bIncludeDerivedClasses = true; GetObjectsOfClass(Archetype->GetClass(), ObjectsToChange, bIncludeDerivedClasses); for ( auto ObjIt = ObjectsToChange.CreateIterator(); ObjIt; ++ObjIt ) { UObject *Object = *ObjIt; UWorld *World = GEngine->GetWorldFromContextObject( Object, false ); if ( World == NULL || World->WorldType != EWorldType::PIE ) continue; CopyPropertyFromArchetype( Object, Archetype, PropertyName ); } */ }
/** * Exports the property values for the specified object as text to the output device. * * @param Context Context from which the set of 'inner' objects is extracted. If NULL, an object iterator will be used. * @param Out the output device to send the exported text to * @param ObjectClass the class of the object to dump properties for * @param Object the address of the object to dump properties for * @param Indent number of spaces to prepend to each line of output * @param DiffClass the class to use for comparing property values when delta export is desired. * @param Diff the address of the object to use for determining whether a property value should be exported. If the value in Object matches the corresponding * value in Diff, it is not exported. Specify NULL to export all properties. * @param Parent the UObject corresponding to Object * @param PortFlags flags used for modifying the output and/or behavior of the export */ void ExportProperties ( const FExportObjectInnerContext* Context, FOutputDevice& Out, UClass* ObjectClass, uint8* Object, int32 Indent, UClass* DiffClass, uint8* Diff, UObject* Parent, uint32 PortFlags, UObject* ExportRootScope ) { FString ThisName = TEXT("(none)"); check(ObjectClass != NULL); for( UProperty* Property = ObjectClass->PropertyLink; Property; Property = Property->PropertyLinkNext ) { if (!Property->ShouldPort(PortFlags)) continue; ThisName = Property->GetName(); UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property); UObjectPropertyBase* ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(Property) : NULL; const uint32 ExportFlags = PortFlags | PPF_Delimited; if ( ArrayProperty != NULL ) { // Export dynamic array. UProperty* InnerProp = ArrayProperty->Inner; ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(InnerProp) : NULL; // This is used as the default value in the case of an array property that has // fewer elements than the exported object. uint8* StructDefaults = NULL; UStructProperty* StructProperty = Cast<UStructProperty>(InnerProp); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { void* Arr = Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex); FScriptArrayHelper ArrayHelper(ArrayProperty, Arr); void* DiffArr = NULL; if( DiffClass ) { DiffArr = Property->ContainerPtrToValuePtrForDefaults<void>(DiffClass, Diff, PropertyArrayIndex); } // we won't use this if DiffArr is NULL, but we have to set it up to something FScriptArrayHelper DiffArrayHelper(ArrayProperty, DiffArr); bool bAnyElementDiffered = false; for( int32 DynamicArrayIndex=0; DynamicArrayIndex<ArrayHelper.Num(); DynamicArrayIndex++ ) { FString Value; // compare each element's value manually so that elements which match the NULL value for the array's inner property type // but aren't in the diff array are still exported uint8* SourceData = ArrayHelper.GetRawPtr(DynamicArrayIndex); uint8* DiffData = DiffArr && DynamicArrayIndex < DiffArrayHelper.Num() ? DiffArrayHelper.GetRawPtr(DynamicArrayIndex) : StructDefaults; bool bExportItem = DiffData == NULL || (DiffData != SourceData && !InnerProp->Identical(SourceData, DiffData, ExportFlags)); if ( bExportItem ) { bAnyElementDiffered = true; InnerProp->ExportTextItem(Value, SourceData, DiffData, Parent, ExportFlags, ExportRootScope); if(ExportObjectProp) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(ArrayHelper.GetRawPtr(DynamicArrayIndex)); check(!Obj || Obj->IsValidLowLevel()); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component // Is this an array of components? if ( InnerProp->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex, *Value ); } // if some other element has already been determined to differ from the defaults, then export this item with no data so that // the different array's size is maintained on import (this item will get the default values for that index, if any) // however, if no elements of the array have changed, we still don't want to export anything // so that the array size will also be taken from the defaults, which won't be the case if any element is exported else if (bAnyElementDiffered) { Out.Logf( TEXT("%s%s(%i)=()\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex ); } } } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } } else { for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { FString Value; // Export single element. uint8* DiffData = (DiffClass && Property->IsInContainer(DiffClass->GetPropertiesSize())) ? Diff : NULL; if( Property->ExportText_InContainer( PropertyArrayIndex, Value, Object, DiffData, Parent, ExportFlags, ExportRootScope ) ) { if ( ExportObjectProp ) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex)); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component if ( Property->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } if( Property->ArrayDim == 1 ) { Out.Logf( TEXT("%s%s=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), *Value ); } else { Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), PropertyArrayIndex, *Value ); } } } } } // Allows to import/export C++ properties in case the automatic unreal script mesh wouldn't work. Parent->ExportCustomProperties(Out, Indent); }