static void AddNamedValuesFromObject(const UObject* Ob, TArray<FEnvNamedValue>& NamedValues, TArray<FName>& RequiredParams) { if (Ob == NULL) { return; } for (UProperty* TestProperty = Ob->GetClass()->PropertyLink; TestProperty; TestProperty = TestProperty->PropertyLinkNext) { UStructProperty* TestStruct = Cast<UStructProperty>(TestProperty); if (TestStruct == NULL) { continue; } FString TypeDesc = TestStruct->GetCPPType(NULL, CPPF_None); if (TypeDesc.Contains(GET_STRUCT_NAME_CHECKED(FEnvIntParam))) { const FEnvIntParam* PropertyValue = TestStruct->ContainerPtrToValuePtr<FEnvIntParam>(Ob); AddNamedValue(PropertyValue->ParamName, EEnvQueryParam::Int, *((float*)&PropertyValue->Value), NamedValues, RequiredParams); } else if (TypeDesc.Contains(GET_STRUCT_NAME_CHECKED(FEnvFloatParam))) { const FEnvFloatParam* PropertyValue = TestStruct->ContainerPtrToValuePtr<FEnvFloatParam>(Ob); AddNamedValue(PropertyValue->ParamName, EEnvQueryParam::Float, PropertyValue->Value, NamedValues, RequiredParams); } else if (TypeDesc.Contains(GET_STRUCT_NAME_CHECKED(FEnvBoolParam))) { const FEnvBoolParam* PropertyValue = TestStruct->ContainerPtrToValuePtr<FEnvBoolParam>(Ob); AddNamedValue(PropertyValue->ParamName, EEnvQueryParam::Bool, PropertyValue->Value ? 1.0f : -1.0f, NamedValues, RequiredParams); } } }
void UK2Node_FunctionEntry::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UEdGraphPin* OldStartExecPin = nullptr; if(Pins[0]->LinkedTo.Num()) { OldStartExecPin = Pins[0]->LinkedTo[0]; } UEdGraphPin* LastActiveOutputPin = Pins[0]; // Only look for FunctionEntry nodes who were duplicated and have a source object if ( UK2Node_FunctionEntry* OriginalNode = Cast<UK2Node_FunctionEntry>(CompilerContext.MessageLog.FindSourceObject(this)) ) { check(OriginalNode->GetOuter()); // Find the associated UFunction UFunction* Function = FindField<UFunction>(CompilerContext.Blueprint->SkeletonGeneratedClass, *OriginalNode->GetOuter()->GetName()); for (TFieldIterator<UProperty> It(Function); It; ++It) { if (const UProperty* Property = *It) { for (auto& LocalVar : LocalVariables) { if (LocalVar.VarName == Property->GetFName() && !LocalVar.DefaultValue.IsEmpty()) { // Add a variable set node for the local variable and hook it up immediately following the entry node or the last added local variable UK2Node_VariableSet* VariableSetNode = CompilerContext.SpawnIntermediateNode<UK2Node_VariableSet>(this, SourceGraph); VariableSetNode->SetFromProperty(Property, false); Schema->ConfigureVarNode(VariableSetNode, LocalVar.VarName, Function, CompilerContext.Blueprint); VariableSetNode->AllocateDefaultPins(); CompilerContext.MessageLog.NotifyIntermediateObjectCreation(VariableSetNode, this); if(UEdGraphPin* SetPin = VariableSetNode->FindPin(Property->GetName())) { if(LocalVar.VarType.bIsArray) { TSharedPtr<FStructOnScope> StructData = MakeShareable(new FStructOnScope(Function)); FBlueprintEditorUtils::PropertyValueFromString(Property, LocalVar.DefaultValue, StructData->GetStructMemory()); // Create a Make Array node to setup the array's defaults UK2Node_MakeArray* MakeArray = CompilerContext.SpawnIntermediateNode<UK2Node_MakeArray>(this, SourceGraph); MakeArray->AllocateDefaultPins(); MakeArray->GetOutputPin()->MakeLinkTo(SetPin); MakeArray->PostReconstructNode(); const UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property); check(ArrayProperty); FScriptArrayHelper_InContainer ArrayHelper(ArrayProperty, StructData->GetStructMemory()); FScriptArrayHelper_InContainer DefaultArrayHelper(ArrayProperty, StructData->GetStructMemory()); uint8* StructDefaults = NULL; UStructProperty* StructProperty = dynamic_cast<UStructProperty*>(ArrayProperty->Inner); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } // Go through each element in the array to set the default value for( int32 ArrayIndex = 0 ; ArrayIndex < ArrayHelper.Num() ; ArrayIndex++ ) { uint8* PropData = ArrayHelper.GetRawPtr(ArrayIndex); // Always use struct defaults if the inner is a struct, for symmetry with the import of array inner struct defaults uint8* PropDefault = ( StructProperty != NULL ) ? StructDefaults : ( ( StructData->GetStructMemory() && DefaultArrayHelper.Num() > ArrayIndex ) ? DefaultArrayHelper.GetRawPtr(ArrayIndex) : NULL ); // Retrieve the element's default value FString DefaultValue; FBlueprintEditorUtils::PropertyValueToString(ArrayProperty->Inner, PropData, DefaultValue); if(ArrayIndex > 0) { MakeArray->AddInputPin(); } // Add one to the index for the pin to set the default on to skip the output pin Schema->TrySetDefaultValue(*MakeArray->Pins[ArrayIndex + 1], DefaultValue); } } else { // Set the default value Schema->TrySetDefaultValue(*SetPin, LocalVar.DefaultValue); } } LastActiveOutputPin->BreakAllPinLinks(); LastActiveOutputPin->MakeLinkTo(VariableSetNode->Pins[0]); LastActiveOutputPin = VariableSetNode->Pins[1]; } } } } // Finally, hook up the last node to the old node the function entry node was connected to if(OldStartExecPin) { LastActiveOutputPin->MakeLinkTo(OldStartExecPin); } } }
FTrackInstancePropertyBindings::FPropertyAddress FTrackInstancePropertyBindings::FindPropertyRecursive( const UObject* Object, void* BasePointer, UStruct* InStruct, TArray<FString>& InPropertyNames, uint32 Index ) const { UProperty* Property = FindField<UProperty>(InStruct, *InPropertyNames[Index]); FTrackInstancePropertyBindings::FPropertyAddress NewAddress; UStructProperty* StructProp = Cast<UStructProperty>( Property ); if( StructProp ) { NewAddress.Property = StructProp; NewAddress.Address = BasePointer; if( InPropertyNames.IsValidIndex(Index+1) ) { void* StructContainer = StructProp->ContainerPtrToValuePtr<void>(BasePointer); return FindPropertyRecursive( Object, StructContainer, StructProp->Struct, InPropertyNames, Index+1 ); } else { check( StructProp->GetName() == InPropertyNames[Index] ); } } else if( Property ) { NewAddress.Property = Property; NewAddress.Address = BasePointer; } return NewAddress; }
bool FAIDataProviderStructValue::IsMatchingType(UProperty* PropType) const { UStructProperty* StructProp = Cast<UStructProperty>(PropType); if (StructProp) { // skip inital "struct " FString CPPType = StructProp->GetCPPType(nullptr, CPPF_None).Mid(8); return CPPType == StructName; } return false; }
/** * 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); }
bool FObjectReplicator::ReceivedBunch( FInBunch &Bunch, const FReplicationFlags & RepFlags ) { UObject * Object = GetObject(); UPackageMap * PackageMap = OwningChannel->Connection->PackageMap; if ( Object == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Object == NULL" ) ); return false; } const bool bIsServer = ( OwningChannel->Connection->Driver->ServerConnection == NULL ); FClassNetCache * ClassCache = PackageMap->GetClassNetCache( ObjectClass ); if ( ClassCache == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: ClassCache == NULL: %s" ), *Object->GetFullName() ); return false; } bool bThisBunchReplicatedProperties = false; // First RepIndex. int32 RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 ); if ( Bunch.IsError() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 1: %s" ), *Object->GetFullName() ); return false; } if ( RepIndex == ClassCache->GetMaxIndex() ) { // There are no actual replicated properties or functions in this bunch. That is ok - we may have gotten this // actor/subobject because we want the client to spawn one (but we arent actually replicating properties on it) return true; } if ( RepIndex > ClassCache->GetMaxIndex() ) { // We shouldn't be receiving this bunch of this object has no properties or RPC functions to process UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() ); return false; } FFieldNetCache * FieldCache = ClassCache->GetFromIndex( RepIndex ); if ( FieldCache == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() ); return false; } while ( FieldCache ) { // Receive properties from the net. UProperty * ReplicatedProp = NULL; int32 LastIndex = 0; while ( FieldCache && ( ReplicatedProp = Cast< UProperty >( FieldCache->Field ) ) != NULL ) { NET_CHECKSUM( Bunch ); // Server shouldn't receive properties. if ( bIsServer ) { UE_LOG( LogNet, Error, TEXT( "Server received unwanted property value %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() ); return false; } bThisBunchReplicatedProperties = true; if ( !bHasReplicatedProperties ) { bHasReplicatedProperties = true; // Persistent, not reset until PostNetReceive is called PreNetReceive(); } bool DebugProperty = false; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) { static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Replication.DebugProperty")); if (CVar && !CVar->GetString().IsEmpty() && ReplicatedProp->GetName().Contains(CVar->GetString()) ) { UE_LOG(LogNet, Log, TEXT("Replicating Property[%d] %s on %s"), RepIndex, *ReplicatedProp->GetName(), *Object->GetName()); DebugProperty = true; } } #endif if ( !Retirement[ ReplicatedProp->RepIndex ].CustomDelta ) { // We hijack a non custom delta property to signify we are using FRepLayout to read the entire property block FPropertyRetirement & Retire = Retirement[ ReplicatedProp->RepIndex ]; bool bDiscardLayout = false; if ( Bunch.PacketId >= Retire.InPacketId ) //!! problem with reliable pkts containing dynamic references, being retransmitted, and overriding newer versions. Want "OriginalPacketId" for retransmissions? { // Receive this new property. Retire.InPacketId = Bunch.PacketId; } else { bDiscardLayout = true; } if ( !RepLayout->ReceiveProperties( ObjectClass, RepState, (void*)Object, Bunch, bDiscardLayout ) ) { UE_LOG( LogNet, Error, TEXT( "ReceiveProperties FAILED %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() ); return false; } } else { // Receive array index. int32 Element = 0; if ( ReplicatedProp->ArrayDim != 1 ) { // Serialize index as delta from previous index to increase chance we'll only use 1 byte uint32 idx; Bunch.SerializeIntPacked( idx ); Element = static_cast< int32 >( idx ) + LastIndex; LastIndex = Element; if ( Element >= ReplicatedProp->ArrayDim ) { UE_LOG( LogNet, Error, TEXT( "Element index too large %s in %s" ), *ReplicatedProp->GetName(), *Object->GetFullName() ); return false; } } // Pointer to destination. uint8 * DestObj = (uint8*)Object; uint8 * DestRecent = RepState->StaticBuffer.Num() ? RepState->StaticBuffer.GetTypedData() : NULL; // Check property ordering. FPropertyRetirement & Retire = Retirement[ ReplicatedProp->RepIndex + Element ]; if ( Bunch.PacketId >= Retire.InPacketId ) //!! problem with reliable pkts containing dynamic references, being retransmitted, and overriding newer versions. Want "OriginalPacketId" for retransmissions? { // Receive this new property. Retire.InPacketId = Bunch.PacketId; } else { // Skip this property, because it's out-of-date. UE_LOG( LogNetTraffic, Log, TEXT( "Received out-of-date %s" ), *ReplicatedProp->GetName() ); DestObj = NULL; DestRecent = NULL; } FMemMark Mark(FMemStack::Get()); uint8 * Data = DestObj ? ReplicatedProp->ContainerPtrToValuePtr<uint8>(DestObj, Element) : NewZeroed<uint8>(FMemStack::Get(),ReplicatedProp->ElementSize); TArray<uint8> MetaData; PTRINT Offset = 0; // Copy current value over to Recent for comparison if ( DestRecent ) { Offset = ReplicatedProp->ContainerPtrToValuePtr<uint8>(DestRecent, Element) - DestRecent; check( Offset >= 0 && Offset < RepState->StaticBuffer.Num() ); //@todo if we move properties outside of the memory block, then this will not work anyway ReplicatedProp->CopySingleValue( DestRecent + Offset, Data ); } // Receive custom delta property. UStructProperty * StructProperty = Cast< UStructProperty >( ReplicatedProp ); if ( StructProperty == NULL ) { // This property isn't custom delta UE_LOG( LogNetTraffic, Error, TEXT( "Property isn't custom delta %s" ), *ReplicatedProp->GetName() ); return false; } UScriptStruct * InnerStruct = StructProperty->Struct; if ( !( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative ) ) { // This property isn't custom delta UE_LOG( LogNetTraffic, Error, TEXT( "Property isn't custom delta %s" ), *ReplicatedProp->GetName() ); return false; } UScriptStruct::ICppStructOps * CppStructOps = InnerStruct->GetCppStructOps(); check( CppStructOps ); check( !InnerStruct->InheritedCppStructOps() ); FNetDeltaSerializeInfo Parms; FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver ); Parms.DebugName = StructProperty->GetName(); Parms.Struct = InnerStruct; Parms.Map = PackageMap; Parms.InArchive = &Bunch; Parms.NetSerializeCB = &NetSerializeCB; // Call the custom delta serialize function to handle it CppStructOps->NetDeltaSerialize( Parms, Data ); if ( Bunch.IsError() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: NetDeltaSerialize - Bunch.IsError() == true: %s" ), *Object->GetFullName() ); return false; } // See if it changed from our local value bool PropertyChanged = true; if ( DestRecent ) { // POD types can do a memcmp with a call to Identical if ( ReplicatedProp->Identical( DestRecent + Offset, Data ) ) { PropertyChanged = false; } } Mark.Pop(); // Successfully received it. UE_LOG( LogNetTraffic, Log, TEXT( " %s - %s - Change: %d" ), *Object->GetName(), *ReplicatedProp->GetName(), PropertyChanged ); // Notify the Object if this var is RepNotify if ( PropertyChanged ) { QueuePropertyRepNotify( Object, ReplicatedProp, Element, MetaData ); } } // Next. RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 ); if ( Bunch.IsError() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 2: %s" ), *Object->GetFullName() ); return false; } if ( RepIndex > ClassCache->GetMaxIndex() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() ); return false; } if ( RepIndex == ClassCache->GetMaxIndex() ) { // We're done FieldCache = NULL; } else { FieldCache = ClassCache->GetFromIndex( RepIndex ); if ( FieldCache == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() ); return false; } } } // Handle function calls. if ( FieldCache && Cast< UFunction >( FieldCache->Field ) ) { FName Message = FieldCache->Field->GetFName(); UFunction * Function = Object->FindFunction( Message ); if ( Function == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Function == NULL: %s" ), *Object->GetFullName() ); return false; } if ( ( Function->FunctionFlags & FUNC_Net ) == 0 ) { UE_LOG( LogNet, Error, TEXT( "Rejected non RPC function %s in %s" ), *Message.ToString(), *Object->GetFullName() ); return false; } if ( ( Function->FunctionFlags & ( bIsServer ? FUNC_NetServer : ( FUNC_NetClient | FUNC_NetMulticast ) ) ) == 0 ) { UE_LOG( LogNet, Error, TEXT( "Rejected RPC function due to access rights %s in %s" ), *Message.ToString(), *Object->GetFullName() ); return false; } UE_LOG( LogNetTraffic, Log, TEXT( " Received RPC: %s" ), *Message.ToString() ); // Get the parameters. FMemMark Mark(FMemStack::Get()); uint8* Parms = new(FMemStack::Get(),MEM_Zeroed,Function->ParmsSize)uint8; // Use the replication layout to receive the rpc parameter values TSharedPtr<FRepLayout> FuncRepLayout = OwningChannel->Connection->Driver->GetFunctionRepLayout( Function ); FuncRepLayout->ReceivePropertiesForRPC( Object, Function, OwningChannel, Bunch, Parms ); if ( Bunch.IsError() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: ReceivePropertiesForRPC - Bunch.IsError() == true: %s" ), *Object->GetFullName() ); return false; } // validate that the function is callable here const bool bCanExecute = ( ( !bIsServer || RepFlags.bNetOwner ) ); // we are client or net owner if ( bCanExecute ) { // Call the function. RPC_ResetLastFailedReason(); Object->ProcessEvent( Function, Parms ); if ( RPC_GetLastFailedReason() != NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RPC_GetLastFailedReason: %s" ), RPC_GetLastFailedReason() ); return false; } } else { UE_LOG( LogNet, Warning, TEXT( "Rejected unwanted function %s in %s" ), *Message.ToString(), *Object->GetFullName() ); if ( !OwningChannel->Connection->TrackLogsPerSecond() ) // This will disconnect the client if we get her too often { return false; } } // Destroy the parameters. //warning: highly dependent on UObject::ProcessEvent freeing of parms! for ( UProperty * Destruct=Function->DestructorLink; Destruct; Destruct=Destruct->DestructorLinkNext ) { if( Destruct->IsInContainer(Function->ParmsSize) ) { Destruct->DestroyValue_InContainer(Parms); } } Mark.Pop(); if ( Object == NULL || Object->IsPendingKill() ) { // replicated function destroyed Object return true; // FIXME: Should this return false to kick connection? Seems we'll cause a read misalignment here if we don't } // Next. RepIndex = Bunch.ReadInt( ClassCache->GetMaxIndex() + 1 ); if ( Bunch.IsError() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Error reading bunch 2: %s" ), *Object->GetFullName() ); return false; } if ( RepIndex > ClassCache->GetMaxIndex() ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: RepIndex too large: %s" ), *Object->GetFullName() ); return false; } if ( RepIndex == ClassCache->GetMaxIndex() ) { // We're done FieldCache = NULL; } else { FieldCache = ClassCache->GetFromIndex( RepIndex ); if ( FieldCache == NULL ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: FieldCache == NULL: %s" ), *Object->GetFullName() ); return false; } } } else if ( FieldCache ) { UE_LOG( LogNet, Error, TEXT( "ReceivedBunch: Invalid replicated field %i in %s" ), RepIndex, *Object->GetFullName() ); return false; } } return true; }
bool FObjectReplicator::ReceivedBunch( FInBunch& Bunch, const FReplicationFlags& RepFlags, bool& bOutHasUnmapped ) { UObject* Object = GetObject(); if ( Object == NULL ) { UE_LOG(LogNet, Verbose, TEXT("ReceivedBunch: Object == NULL")); return false; } UPackageMap * PackageMap = OwningChannel->Connection->PackageMap; const bool bIsServer = ( OwningChannel->Connection->Driver->ServerConnection == NULL ); const FClassNetCache * ClassCache = OwningChannel->Connection->Driver->NetCache->GetClassNetCache( ObjectClass ); if ( ClassCache == NULL ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: ClassCache == NULL: %s"), *Object->GetFullName()); return false; } bool bThisBunchReplicatedProperties = false; // Read first field const FFieldNetCache * FieldCache = ReadField( ClassCache, Bunch ); if ( Bunch.IsError() ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 1: %s"), *Object->GetFullName()); return false; } if ( FieldCache == NULL ) { // There are no actual replicated properties or functions in this bunch. That is ok - we may have gotten this // actor/sub-object because we want the client to spawn one (but we aren't actually replicating properties on it) return true; } while ( FieldCache ) { // Receive properties from the net. UProperty* ReplicatedProp = NULL; while ( FieldCache && ( ReplicatedProp = Cast< UProperty >( FieldCache->Field ) ) != NULL ) { NET_CHECKSUM( Bunch ); // Server shouldn't receive properties. if ( bIsServer ) { UE_LOG(LogNet, Error, TEXT("Server received unwanted property value %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName()); return false; } bThisBunchReplicatedProperties = true; if ( !bHasReplicatedProperties ) { bHasReplicatedProperties = true; // Persistent, not reset until PostNetReceive is called PreNetReceive(); } bool DebugProperty = false; #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) { static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.Replication.DebugProperty")); if (CVar && !CVar->GetString().IsEmpty() && ReplicatedProp->GetName().Contains(CVar->GetString()) ) { UE_LOG(LogRep, Log, TEXT("Replicating Property[%d] %s on %s"), ReplicatedProp->RepIndex, *ReplicatedProp->GetName(), *Object->GetName()); DebugProperty = true; } } #endif if ( !Retirement[ ReplicatedProp->RepIndex ].CustomDelta ) { bool bLocalHasUnmapped = false; // We hijack a non custom delta property to signify we are using FRepLayout to read the entire property block if ( !RepLayout->ReceiveProperties( ObjectClass, RepState, (void*)Object, Bunch, bLocalHasUnmapped ) ) { UE_LOG(LogRep, Error, TEXT("ReceiveProperties FAILED %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName()); return false; } if ( bLocalHasUnmapped ) { bOutHasUnmapped = true; } } else { // Receive array index. uint32 Element = 0; if ( ReplicatedProp->ArrayDim != 1 ) { check( ReplicatedProp->ArrayDim >= 2 ); Bunch.SerializeIntPacked( Element ); if ( Element >= (uint32)ReplicatedProp->ArrayDim ) { UE_LOG(LogRep, Error, TEXT("Element index too large %s in %s"), *ReplicatedProp->GetName(), *Object->GetFullName()); return false; } } // Pointer to destination. uint8* Data = ReplicatedProp->ContainerPtrToValuePtr<uint8>((uint8*)Object, Element); TArray<uint8> MetaData; const PTRINT DataOffset = Data - (uint8*)Object; // Receive custom delta property. UStructProperty * StructProperty = Cast< UStructProperty >( ReplicatedProp ); if ( StructProperty == NULL ) { // This property isn't custom delta UE_LOG(LogRepTraffic, Error, TEXT("Property isn't custom delta %s"), *ReplicatedProp->GetName()); return false; } UScriptStruct * InnerStruct = StructProperty->Struct; if ( !( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative ) ) { // This property isn't custom delta UE_LOG(LogRepTraffic, Error, TEXT("Property isn't custom delta %s"), *ReplicatedProp->GetName()); return false; } UScriptStruct::ICppStructOps * CppStructOps = InnerStruct->GetCppStructOps(); check( CppStructOps ); check( !InnerStruct->InheritedCppStructOps() ); FNetDeltaSerializeInfo Parms; FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver ); Parms.DebugName = StructProperty->GetName(); Parms.Struct = InnerStruct; Parms.Map = PackageMap; Parms.Reader = &Bunch; Parms.NetSerializeCB = &NetSerializeCB; // Call the custom delta serialize function to handle it CppStructOps->NetDeltaSerialize( Parms, Data ); if ( Bunch.IsError() ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: NetDeltaSerialize - Bunch.IsError() == true: %s"), *Object->GetFullName()); return false; } if ( Parms.bOutHasMoreUnmapped ) { UnmappedCustomProperties.Add( DataOffset, StructProperty ); bOutHasUnmapped = true; } // Successfully received it. UE_LOG(LogRepTraffic, Log, TEXT(" %s - %s"), *Object->GetName(), *ReplicatedProp->GetName()); // Notify the Object if this var is RepNotify QueuePropertyRepNotify( Object, ReplicatedProp, Element, MetaData ); } // Read next field FieldCache = ReadField( ClassCache, Bunch ); if ( Bunch.IsError() ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 2: %s"), *Object->GetFullName()); return false; } } // Handle function calls. if ( FieldCache && Cast< UFunction >( FieldCache->Field ) ) { FName Message = FieldCache->Field->GetFName(); UFunction * Function = Object->FindFunction( Message ); if ( Function == NULL ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Function == NULL: %s"), *Object->GetFullName()); return false; } if ( ( Function->FunctionFlags & FUNC_Net ) == 0 ) { UE_LOG(LogRep, Error, TEXT("Rejected non RPC function %s in %s"), *Message.ToString(), *Object->GetFullName()); return false; } if ( ( Function->FunctionFlags & ( bIsServer ? FUNC_NetServer : ( FUNC_NetClient | FUNC_NetMulticast ) ) ) == 0 ) { UE_LOG(LogRep, Error, TEXT("Rejected RPC function due to access rights %s in %s"), *Message.ToString(), *Object->GetFullName()); return false; } UE_LOG(LogRepTraffic, Log, TEXT(" Received RPC: %s"), *Message.ToString()); // Get the parameters. FMemMark Mark(FMemStack::Get()); uint8* Parms = new(FMemStack::Get(),MEM_Zeroed,Function->ParmsSize)uint8; // Use the replication layout to receive the rpc parameter values TSharedPtr<FRepLayout> FuncRepLayout = OwningChannel->Connection->Driver->GetFunctionRepLayout( Function ); FuncRepLayout->ReceivePropertiesForRPC( Object, Function, OwningChannel, Bunch, Parms ); if ( Bunch.IsError() ) { UE_LOG(LogRep, Error, TEXT("ReceivedBunch: ReceivePropertiesForRPC - Bunch.IsError() == true: Function: %s, Object: %s"), *Message.ToString(), *Object->GetFullName()); return false; } // validate that the function is callable here const bool bCanExecute = ( !bIsServer || RepFlags.bNetOwner ); // we are client or net owner if ( bCanExecute ) { // Call the function. RPC_ResetLastFailedReason(); Object->ProcessEvent( Function, Parms ); if ( RPC_GetLastFailedReason() != NULL ) { UE_LOG(LogRep, Error, TEXT("ReceivedBunch: RPC_GetLastFailedReason: %s"), RPC_GetLastFailedReason()); return false; } } else { UE_LOG(LogRep, Verbose, TEXT("Rejected unwanted function %s in %s"), *Message.ToString(), *Object->GetFullName()); if ( !OwningChannel->Connection->TrackLogsPerSecond() ) // This will disconnect the client if we get here too often { UE_LOG(LogRep, Error, TEXT("Rejected too many unwanted functions %s in %s"), *Message.ToString(), *Object->GetFullName()); return false; } } // Destroy the parameters. //warning: highly dependent on UObject::ProcessEvent freeing of parms! for ( UProperty * Destruct=Function->DestructorLink; Destruct; Destruct=Destruct->DestructorLinkNext ) { if( Destruct->IsInContainer(Function->ParmsSize) ) { Destruct->DestroyValue_InContainer(Parms); } } Mark.Pop(); if ( Object == NULL || Object->IsPendingKill() ) { // replicated function destroyed Object return true; // FIXME: Should this return false to kick connection? Seems we'll cause a read misalignment here if we don't } // Next. FieldCache = ReadField( ClassCache, Bunch ); if ( Bunch.IsError() ) { UE_LOG(LogNet, Error, TEXT("ReceivedBunch: Error reading field 3: %s"), *Object->GetFullName()); return false; } } else if ( FieldCache ) { UE_LOG(LogRep, Error, TEXT("ReceivedBunch: Invalid replicated field %i in %s"), FieldCache->FieldNetIndex, *Object->GetFullName()); return false; } } return true; }
void FObjectReplicator::UpdateUnmappedObjects( bool & bOutHasMoreUnmapped ) { UObject* Object = GetObject(); if ( Object == NULL || Object->IsPendingKill() ) { bOutHasMoreUnmapped = false; return; } if ( Connection->State == USOCK_Closed ) { UE_LOG(LogNet, Warning, TEXT("FObjectReplicator::UpdateUnmappedObjects: Connection->State == USOCK_Closed")); return; } checkf( RepState->RepNotifies.Num() == 0, TEXT("Failed RepState RepNotifies check. Num=%d. Object=%s"), RepState->RepNotifies.Num(), *Object->GetFullName() ); checkf( RepNotifies.Num() == 0, TEXT("Failed replicator RepNotifies check. Num=%d. Object=%s."), RepNotifies.Num(), *Object->GetFullName() ); bool bSomeObjectsWereMapped = false; // Let the rep layout update any unmapped properties RepLayout->UpdateUnmappedObjects( RepState, Connection->PackageMap, Object, bSomeObjectsWereMapped, bOutHasMoreUnmapped ); // Update unmapped objects for custom properties (currently just fast tarray) for ( auto It = UnmappedCustomProperties.CreateIterator(); It; ++It ) { const int32 Offset = It.Key(); UStructProperty* StructProperty = It.Value(); UScriptStruct* InnerStruct = StructProperty->Struct; check( InnerStruct->StructFlags & STRUCT_NetDeltaSerializeNative ); UScriptStruct::ICppStructOps* CppStructOps = InnerStruct->GetCppStructOps(); check( CppStructOps ); check( !InnerStruct->InheritedCppStructOps() ); FNetDeltaSerializeInfo Parms; FNetSerializeCB NetSerializeCB( OwningChannel->Connection->Driver ); Parms.DebugName = StructProperty->GetName(); Parms.Struct = InnerStruct; Parms.Map = Connection->PackageMap; Parms.NetSerializeCB = &NetSerializeCB; Parms.bUpdateUnmappedObjects = true; Parms.bCalledPreNetReceive = bSomeObjectsWereMapped; // RepLayout used this to flag whether PreNetReceive was called Parms.Object = Object; // Call the custom delta serialize function to handle it CppStructOps->NetDeltaSerialize( Parms, (uint8*)Object + Offset ); // Merge in results bSomeObjectsWereMapped |= Parms.bOutSomeObjectsWereMapped; bOutHasMoreUnmapped |= Parms.bOutHasMoreUnmapped; if ( Parms.bOutSomeObjectsWereMapped ) { // If we mapped a property, call the rep notify TArray<uint8> MetaData; QueuePropertyRepNotify( Object, StructProperty, 0, MetaData ); } // If this property no longer has unmapped objects, we can stop checking it if ( !Parms.bOutHasMoreUnmapped ) { It.RemoveCurrent(); } } // Call any rep notifies that need to happen when object pointers change // Pass in false to override the check for queued bunches. Otherwise, if the owning channel has queued bunches, // the RepNotifies will remain in the list and the check for 0 RepNotifies above will fail next time. CallRepNotifies(false); if ( bSomeObjectsWereMapped ) { // If we mapped some objects, make sure to call PostNetReceive (some game code will need to think this was actually replicated to work) PostNetReceive(); } }
void SDetailsViewBase::UpdatePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutBuilderImpl& InDetailLayout, FName CurCategory, FComplexPropertyNode* CurObjectNode) { UProperty* ParentProperty = InNode.GetProperty(); UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty); for (int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex) { TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex); FPropertyNode& ChildNode = *ChildNodePtr; UProperty* Property = ChildNode.GetProperty(); { FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode(); FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode(); if (ObjNode) { // Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them. // When we encounter object property nodes, add their children instead of adding them to the tree. UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, ObjNode); } else if (CategoryNode) { // For category nodes, we just set the current category and recurse through the children UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CategoryNode->GetCategoryName(), CurObjectNode); } else { // Whether or not the property can be visible in the default detail layout bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode); // Whether or not the property is a struct UStructProperty* StructProperty = Cast<UStructProperty>(Property); bool bIsStruct = StructProperty != NULL; static FName ShowOnlyInners("ShowOnlyInnerProperties"); bool bIsChildOfCustomizedStruct = false; bool bIsCustomizedStruct = false; const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL; const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL; if (Struct || ParentStruct) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); if (Struct) { bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis( this ) ); } if (ParentStruct) { bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis( this ) ); } } // Whether or not to push out struct properties to their own categories or show them inside an expandable struct bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners); // Is the property edit inline new const bool bIsEditInlineNew = SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex()); // Is this a property of an array bool bIsChildOfArray = PropertyEditorHelpers::IsChildOfArray(ChildNode); // Edit inline new properties should be visible by default bVisibleByDefault |= bIsEditInlineNew; // Children of arrays are not visible directly, bVisibleByDefault &= !bIsChildOfArray; FPropertyAndParent PropertyAndParent(*Property, ParentProperty); const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent); // Inners of customized in structs should not be taken into consideration for customizing. They are not designed to be individually customized when their parent is already customized if (!bIsChildOfCustomizedStruct) { // Add any object classes with properties so we can ask them for custom property layouts later ClassesWithProperties.Add(Property->GetOwnerStruct()); } // If there is no outer object then the class is the object root and there is only one instance FName InstanceName = NAME_None; if (CurObjectNode && CurObjectNode->GetParentNode()) { InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName(); } else if (ParentStructProp) { InstanceName = ParentStructProp->GetFName(); } // Do not add children of customized in struct properties or arrays if (!bIsChildOfCustomizedStruct && !bIsChildOfArray) { // Get the class property map FClassInstanceToPropertyMap& ClassInstanceMap = ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName()); FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName); if (!PropertyNodeMap.ParentProperty) { PropertyNodeMap.ParentProperty = CurObjectNode; } else { ensure(PropertyNodeMap.ParentProperty == CurObjectNode); } checkSlow(!PropertyNodeMap.Contains(Property->GetFName())); PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr); } if (bVisibleByDefault && bIsUserVisible && !bPushOutStructProps) { FName CategoryName = CurCategory; // For properties inside a struct, add them to their own category unless they just take the name of the parent struct. // In that case push them to the parent category FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property); if (!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName())) { CategoryName = PropertyCatagoryName; } if (IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } // Add a property to the default category FDetailCategoryImpl& CategoryImpl = InDetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } bool bRecurseIntoChildren = !bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them && !bIsCustomizedStruct // Don't recurse into customized structs && !bIsChildOfArray // Do not recurse into arrays, the children are drawn by the array property parent && !bIsEditInlineNew // Edit inline new children are not supported for customization yet && bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either && (!bIsStruct || bPushOutStructProps); // Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category if (bRecurseIntoChildren) { // Built in struct properties or children of arras UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, CurObjectNode); } } } } }
void SDetailsViewBase::UpdateSinglePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutData& LayoutData, FName CurCategory, FComplexPropertyNode* CurObjectNode, bool bEnableFavoriteSystem, bool bUpdateFavoriteSystemOnly) { FDetailLayoutBuilderImpl& DetailLayout = *LayoutData.DetailLayout; UProperty* ParentProperty = InNode.GetProperty(); UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty); for(int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex) { //Use the original value for each child bool LocalUpdateFavoriteSystemOnly = bUpdateFavoriteSystemOnly; TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex); FPropertyNode& ChildNode = *ChildNodePtr; UProperty* Property = ChildNode.GetProperty(); { FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode(); FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode(); if(ObjNode) { // Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them. // When we encounter object property nodes, add their children instead of adding them to the tree. UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, ObjNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } else if(CategoryNode) { if(!LocalUpdateFavoriteSystemOnly) { FName InstanceName = NAME_None; FName CategoryName = CurCategory; FString CategoryDelimiterString; CategoryDelimiterString.AppendChar(FPropertyNodeConstants::CategoryDelimiterChar); if(CurCategory != NAME_None && CategoryNode->GetCategoryName().ToString().Contains(CategoryDelimiterString)) { // This property is child of another property so add it to the parent detail category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } } // For category nodes, we just set the current category and recurse through the children UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CategoryNode->GetCategoryName(), CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } else { // Whether or not the property can be visible in the default detail layout bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode); // Whether or not the property is a struct UStructProperty* StructProperty = Cast<UStructProperty>(Property); bool bIsStruct = StructProperty != NULL; static FName ShowOnlyInners("ShowOnlyInnerProperties"); bool bIsChildOfCustomizedStruct = false; bool bIsCustomizedStruct = false; const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL; const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL; if(Struct || ParentStruct) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); if(Struct) { bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis(this)); } if(ParentStruct) { bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis(this)); } } // Whether or not to push out struct properties to their own categories or show them inside an expandable struct bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners); // Is the property edit inline new const bool bIsEditInlineNew = ChildNode.HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties) || SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex()); // Is this a property of a container property bool bIsChildOfContainer = PropertyEditorHelpers::IsChildOfArray(ChildNode) || PropertyEditorHelpers::IsChildOfSet(ChildNode) || PropertyEditorHelpers::IsChildOfMap(ChildNode); // Edit inline new properties should be visible by default bVisibleByDefault |= bIsEditInlineNew; // Children of arrays are not visible directly, bVisibleByDefault &= !bIsChildOfContainer; FPropertyAndParent PropertyAndParent(*Property, ParentProperty); const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent); // Inners of customized in structs should not be taken into consideration for customizing. They are not designed to be individually customized when their parent is already customized if(!bIsChildOfCustomizedStruct && !LocalUpdateFavoriteSystemOnly) { // Add any object classes with properties so we can ask them for custom property layouts later LayoutData.ClassesWithProperties.Add(Property->GetOwnerStruct()); } // If there is no outer object then the class is the object root and there is only one instance FName InstanceName = NAME_None; if(CurObjectNode && CurObjectNode->GetParentNode()) { InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName(); } else if(ParentStructProp) { InstanceName = ParentStructProp->GetFName(); } // Do not add children of customized in struct properties or arrays if(!bIsChildOfCustomizedStruct && !bIsChildOfContainer && !LocalUpdateFavoriteSystemOnly) { // Get the class property map FClassInstanceToPropertyMap& ClassInstanceMap = LayoutData.ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName()); FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName); if(!PropertyNodeMap.ParentProperty) { PropertyNodeMap.ParentProperty = CurObjectNode; } else { ensure(PropertyNodeMap.ParentProperty == CurObjectNode); } checkSlow(!PropertyNodeMap.Contains(Property->GetFName())); PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr); } bool bCanDisplayFavorite = false; if(bVisibleByDefault && bIsUserVisible && !bPushOutStructProps) { FName CategoryName = CurCategory; // For properties inside a struct, add them to their own category unless they just take the name of the parent struct. // In that case push them to the parent category FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property); if(!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName())) { CategoryName = PropertyCatagoryName; } if(!LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } // Add a property to the default category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } bCanDisplayFavorite = true; if(bEnableFavoriteSystem) { if(bIsCustomizedStruct) { bCanDisplayFavorite = false; //CustomizedStruct child are not categorize since they are under an object but we have to put them in favorite category if the user want to favorite them LocalUpdateFavoriteSystemOnly = true; } else if(ChildNodePtr->IsFavorite()) { //Find or create the favorite category, we have to duplicate favorite property row under this category FString CategoryFavoritesName = TEXT("Favorites"); FName CatFavName = *CategoryFavoritesName; FDetailCategoryImpl& CategoryFavImpl = DetailLayout.DefaultCategory(CatFavName); CategoryFavImpl.SetSortOrder(0); CategoryFavImpl.SetCategoryAsSpecialFavorite(); //Add the property to the favorite FObjectPropertyNode *RootObjectParent = ChildNodePtr->FindRootObjectItemParent(); FName RootInstanceName = NAME_None; if(RootObjectParent != nullptr) { RootInstanceName = RootObjectParent->GetObjectBaseClass()->GetFName(); } if(LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } else { //If the parent has a condition that is not met, make the child as readonly FDetailLayoutCustomization ParentTmpCustomization; ParentTmpCustomization.PropertyRow = MakeShareable(new FDetailPropertyRow(InNode.AsShared(), CategoryFavImpl.AsShared())); if(ParentTmpCustomization.PropertyRow->GetPropertyEditor()->IsPropertyEditingEnabled() == false) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } } } //Duplicate the row CategoryFavImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), RootInstanceName); } if(bIsStruct) { LocalUpdateFavoriteSystemOnly = true; } } } ChildNodePtr->SetCanDisplayFavorite(bCanDisplayFavorite); bool bRecurseIntoChildren = !bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them && !bIsCustomizedStruct // Don't recurse into customized structs && !bIsChildOfContainer // Do not recurse into containers, the children are drawn by the container property parent && !bIsEditInlineNew // Edit inline new children are not supported for customization yet && bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either && (!bIsStruct || bPushOutStructProps); // Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category if(bRecurseIntoChildren || LocalUpdateFavoriteSystemOnly) { // Built in struct properties or children of arras UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } } } } }
void UArrayProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { checkSlow(Inner); FScriptArrayHelper ArrayHelper(this, PropertyValue); FScriptArrayHelper DefaultArrayHelper(this, DefaultValue); uint8* StructDefaults = NULL; UStructProperty* StructProperty = Cast<UStructProperty>(Inner); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } const bool bReadableForm = (0 != (PPF_BlueprintDebugView & PortFlags)); int32 Count = 0; for( int32 i=0; i<ArrayHelper.Num(); i++ ) { ++Count; if(!bReadableForm) { if ( Count == 1 ) { ValueStr += TCHAR('('); } else { ValueStr += TCHAR(','); } } else { if(Count > 1) { ValueStr += TCHAR('\n'); } ValueStr += FString::Printf(TEXT("[%i] "), i); } uint8* PropData = ArrayHelper.GetRawPtr(i); uint8* PropDefault = (DefaultValue && DefaultArrayHelper.Num() > i) ? DefaultArrayHelper.GetRawPtr(i) : StructDefaults; // Do not re-export duplicate data from superclass when exporting to .int file if ( (PortFlags & PPF_LocalizedOnly) != 0 && Inner->Identical(PropData, PropDefault) ) { continue; } Inner->ExportTextItem( ValueStr, PropData, PropDefault, Parent, PortFlags|PPF_Delimited, ExportRootScope ); } if ((Count > 0) && !bReadableForm) { ValueStr += TEXT(")"); } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } }
void UArrayProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { checkSlow(Inner); if (0 != (PortFlags & PPF_ExportCpp)) { FString ExtendedTypeText; FString TypeText = GetCPPType(&ExtendedTypeText, EPropertyExportCPPFlags::CPPF_BlueprintCppBackend); ValueStr += FString::Printf(TEXT("%s%s()"), *TypeText, *ExtendedTypeText); return; } FScriptArrayHelper ArrayHelper(this, PropertyValue); FScriptArrayHelper DefaultArrayHelper(this, DefaultValue); uint8* StructDefaults = NULL; UStructProperty* StructProperty = dynamic_cast<UStructProperty*>(Inner); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } const bool bReadableForm = (0 != (PPF_BlueprintDebugView & PortFlags)); int32 Count = 0; for( int32 i=0; i<ArrayHelper.Num(); i++ ) { ++Count; if(!bReadableForm) { if ( Count == 1 ) { ValueStr += TCHAR('('); } else { ValueStr += TCHAR(','); } } else { if(Count > 1) { ValueStr += TCHAR('\n'); } ValueStr += FString::Printf(TEXT("[%i] "), i); } uint8* PropData = ArrayHelper.GetRawPtr(i); // Always use struct defaults if the inner is a struct, for symmetry with the import of array inner struct defaults uint8* PropDefault = ( StructProperty != NULL ) ? StructDefaults : ( ( DefaultValue && DefaultArrayHelper.Num() > i ) ? DefaultArrayHelper.GetRawPtr(i) : NULL ); Inner->ExportTextItem( ValueStr, PropData, PropDefault, Parent, PortFlags|PPF_Delimited, ExportRootScope ); } if ((Count > 0) && !bReadableForm) { ValueStr += TEXT(")"); } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } }
static void ReplaceStructWithTempDuplicate( UBlueprintGeneratedStruct* StructureToReinstance, TSet<UBlueprint*>& BlueprintsToRecompile, TArray<UBlueprintGeneratedStruct*>& ChangedStructs) { if (StructureToReinstance) { UBlueprintGeneratedStruct* DuplicatedStruct = NULL; { const FString ReinstancedName = FString::Printf(TEXT("STRUCT_REINST_%s"), *StructureToReinstance->GetName()); const FName UniqueName = MakeUniqueObjectName(GetTransientPackage(), UBlueprintGeneratedStruct::StaticClass(), FName(*ReinstancedName)); const bool OldIsDuplicatingClassForReinstancing = GIsDuplicatingClassForReinstancing; GIsDuplicatingClassForReinstancing = true; DuplicatedStruct = (UBlueprintGeneratedStruct*)StaticDuplicateObject(StructureToReinstance, GetTransientPackage(), *UniqueName.ToString(), ~RF_Transactional); GIsDuplicatingClassForReinstancing = OldIsDuplicatingClassForReinstancing; } DuplicatedStruct->Status = EBlueprintStructureStatus::BSS_Duplicate; DuplicatedStruct->SetFlags(RF_Transient); DuplicatedStruct->AddToRoot(); for (TObjectIterator<UStructProperty> PropertyIter(RF_ClassDefaultObject|RF_PendingKill); PropertyIter; ++PropertyIter) { UStructProperty* StructProperty = *PropertyIter; if (StructProperty && (StructureToReinstance == StructProperty->Struct)) { UBlueprint* FoundBlueprint = NULL; if (auto OwnerClass = Cast<UBlueprintGeneratedClass>(StructProperty->GetOwnerClass())) { FoundBlueprint = Cast<UBlueprint>(OwnerClass->ClassGeneratedBy); } else if (auto OwnerStruct = Cast<UBlueprintGeneratedStruct>(StructProperty->GetOwnerStruct())) { check(OwnerStruct != DuplicatedStruct); const bool bValidStruct = (OwnerStruct->GetOutermost() != GetTransientPackage()) && !OwnerStruct->HasAnyFlags(RF_PendingKill) && (EBlueprintStructureStatus::BSS_Duplicate != OwnerStruct->Status.GetValue()); if (bValidStruct) { FoundBlueprint = OwnerStruct->StructGeneratedBy; ChangedStructs.AddUnique(OwnerStruct); } } else { UE_LOG(LogK2Compiler, Warning, TEXT("ReplaceStructWithTempDuplicate unknown owner")); } if (NULL != FoundBlueprint) { StructProperty->Struct = DuplicatedStruct; BlueprintsToRecompile.Add(FoundBlueprint); } } } DuplicatedStruct->RemoveFromRoot(); } }