void UInheritableComponentHandler::ValidateTemplates()
{
	for (int32 Index = 0; Index < Records.Num();)
	{
		bool bIsValidAndNecessary = false;
		{
			FComponentOverrideRecord& Record = Records[Index];
			FComponentKey& ComponentKey = Record.ComponentKey;
			
			FName VarName = ComponentKey.GetSCSVariableName();
			if (ComponentKey.RefreshVariableName())
			{
				FName NewName = ComponentKey.GetSCSVariableName();
				UE_LOG(LogBlueprint, Log, TEXT("ValidateTemplates '%s': variable old name '%s' new name '%s'"),
					*GetPathNameSafe(this), *VarName.ToString(), *NewName.ToString());
				VarName = NewName;

				MarkPackageDirty();
			}

			if (IsRecordValid(Record))
			{
				if (IsRecordNecessary(Record))
				{
					bIsValidAndNecessary = true;
				}
				else
				{
					UE_LOG(LogBlueprint, Log, TEXT("ValidateTemplates '%s': overriden template is unnecessary - component '%s' from '%s'"),
						*GetPathNameSafe(this), *VarName.ToString(), *GetPathNameSafe(ComponentKey.GetComponentOwner()));
				}
			}
			else
			{
				UE_LOG(LogBlueprint, Warning, TEXT("ValidateTemplates '%s': overriden template is invalid - component '%s' from '%s'"),
					*GetPathNameSafe(this), *VarName.ToString(), *GetPathNameSafe(ComponentKey.GetComponentOwner()));
			}
		}

		if (bIsValidAndNecessary)
		{
			++Index;
		}
		else
		{
			Records.RemoveAtSwap(Index);
		}
	}
}
UActorComponent* UInheritableComponentHandler::CreateOverridenComponentTemplate(FComponentKey Key)
{
	for (int32 Index = 0; Index < Records.Num(); ++Index)
	{
		FComponentOverrideRecord& Record = Records[Index];
		if (Record.ComponentKey.Match(Key))
		{
			if (Record.ComponentTemplate)
			{
				return Record.ComponentTemplate;
			}
			Records.RemoveAtSwap(Index);
			break;
		}
	}

	auto BestArchetype = FindBestArchetype(Key);
	if (!BestArchetype)
	{
		UE_LOG(LogBlueprint, Warning, TEXT("CreateOverridenComponentTemplate '%s': cannot find archetype for component '%s' from '%s'"),
			*GetPathNameSafe(this), *Key.GetSCSVariableName().ToString(), *GetPathNameSafe(Key.GetComponentOwner()));
		return NULL;
	}
	ensure(Cast<UBlueprintGeneratedClass>(GetOuter()));
	auto NewComponentTemplate = NewObject<UActorComponent>(
		GetOuter(), BestArchetype->GetClass(), BestArchetype->GetFName(), RF_ArchetypeObject | RF_Public | RF_InheritableComponentTemplate, BestArchetype);

	// HACK: NewObject can return a pre-existing object which will not have been initialized to the archetype.  When we remove the old handlers, we mark them pending
	//       kill so we can identify that situation here (see UE-13987/UE-13990)
	if (NewComponentTemplate->IsPendingKill())
	{
		NewComponentTemplate->ClearFlags(RF_PendingKill);
		UEngine::FCopyPropertiesForUnrelatedObjectsParams CopyParams;
		CopyParams.bDoDelta = false;
		UEngine::CopyPropertiesForUnrelatedObjects(BestArchetype, NewComponentTemplate, CopyParams);
	}

	FComponentOverrideRecord NewRecord;
	NewRecord.ComponentKey = Key;
	NewRecord.ComponentTemplate = NewComponentTemplate;
	Records.Add(NewRecord);

	return NewComponentTemplate;
}
FArchive& FArchiveObjectCrc32::operator<<(class UObject*& Object)
{
	FArchive& Ar = *this;

	if (!Object || !Object->IsIn(RootObject))
	{
		auto UniqueName = GetPathNameSafe(Object);
		Ar << UniqueName;
	}
	else
	{
		ObjectsToSerialize.Enqueue(Object);
	}

	return Ar;
}
FArchive& FArchiveObjectCrc32::operator<<(class UObject*& Object)
{
	FArchive& Ar = *this;

	// No linker should be a part of the object's state
	if (Object && Object->IsA<ULinker>())
	{
		Object = NULL;
	}

	if (!Object || !Object->IsIn(RootObject))
	{
		auto UniqueName = GetPathNameSafe(Object);
		Ar << UniqueName;
	}
	else
	{
		ObjectsToSerialize.Enqueue(Object);
	}

	return Ar;
}
void FBlueprintNativeCodeGenModule::GenerateSingleAsset(UField* ForConversion, const TCHAR* PlatformName, TSharedPtr<FNativizationSummary> NativizationSummary)
{
	IBlueprintCompilerCppBackendModule& BackEndModule = (IBlueprintCompilerCppBackendModule&)IBlueprintCompilerCppBackendModule::Get();
	auto& BackendPCHQuery = BackEndModule.OnPCHFilenameQuery();
	const IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
	FAssetData AssetInfo = Registry.GetAssetByObjectPath(*ForConversion->GetPathName());

	FBlueprintNativeCodeGenPaths TargetPaths = GetManifest(PlatformName).GetTargetPaths();
	BackendPCHQuery.BindLambda([TargetPaths]()->FString
	{
		return TargetPaths.RuntimePCHFilename();
	});

	FConvertedAssetRecord& ConversionRecord = GetManifest(PlatformName).CreateConversionRecord(*ForConversion->GetPathName(), AssetInfo);
	TSharedPtr<FString> HeaderSource(new FString());
	TSharedPtr<FString> CppSource(new FString());

	FBlueprintNativeCodeGenUtils::GenerateCppCode(ForConversion, HeaderSource, CppSource, NativizationSummary);
	bool bSuccess = !HeaderSource->IsEmpty() || !CppSource->IsEmpty();
	// Run the cpp first, because we cue off of the presence of a header for a valid conversion record (see
	// FConvertedAssetRecord::IsValid)
	if (!CppSource->IsEmpty())
	{
		if (!FFileHelper::SaveStringToFile(*CppSource, *ConversionRecord.GeneratedCppPath, ForcedEncoding()))
		{
			bSuccess &= false;
			ConversionRecord.GeneratedCppPath.Empty();
		}
		CppSource->Empty(CppSource->Len());
	}
	else
	{
		ConversionRecord.GeneratedCppPath.Empty();
	}

	if (bSuccess && !HeaderSource->IsEmpty())
	{
		if (!FFileHelper::SaveStringToFile(*HeaderSource, *ConversionRecord.GeneratedHeaderPath, ForcedEncoding()))
		{
			bSuccess &= false;
			ConversionRecord.GeneratedHeaderPath.Empty();
		}
		HeaderSource->Empty(HeaderSource->Len());
	}
	else
	{
		ConversionRecord.GeneratedHeaderPath.Empty();
	}

	if (bSuccess)
	{
		GetManifest(PlatformName).GatherModuleDependencies(ForConversion->GetOutermost());
	}
	else
	{
		UE_LOG(LogBlueprintCodeGen, Error, TEXT("FBlueprintNativeCodeGenModule::GenerateSingleAsset error: %s"), *GetPathNameSafe(ForConversion));
	}

	BackendPCHQuery.Unbind();
}
Exemple #6
0
bool GetMovementBaseTransform(const UPrimitiveComponent* MovementBase, const FName BoneName, FVector& OutLocation, FQuat& OutQuat)
{
    if (MovementBase)
    {
        if (BoneName != NAME_None)
        {
            const USkeletalMeshComponent* SkeletalBase = Cast<USkeletalMeshComponent>(MovementBase);
            if (SkeletalBase)
            {
                const int32 BoneIndex = SkeletalBase->GetBoneIndex(BoneName);
                if (BoneIndex != INDEX_NONE)
                {
                    const FTransform BoneTransform = SkeletalBase->GetBoneTransform(BoneIndex);
                    OutLocation = BoneTransform.GetLocation();
                    OutQuat = BoneTransform.GetRotation();
                    return true;
                }

                UE_LOG(LogCharacter, Warning, TEXT("GetMovementBaseTransform(): Invalid bone '%s' for SkeletalMeshComponent base %s"), *BoneName.ToString(), *GetPathNameSafe(MovementBase));
                return false;
            }
            // TODO: warn if not a skeletal mesh but providing bone index.
        }

        // No bone supplied
        OutLocation = MovementBase->GetComponentLocation();
        OutQuat = MovementBase->GetComponentQuat();
        return true;
    }

    // NULL MovementBase
    OutLocation = FVector::ZeroVector;
    OutQuat = FQuat::Identity;
    return false;
}
float UProjectileMovementComponent::GetSimulationTimeStep(float RemainingTime, int32 Iterations) const
{
	if (RemainingTime > MaxSimulationTimeStep)
	{
		if (Iterations < MaxSimulationIterations)
		{
			// Subdivide moves to be no longer than MaxSimulationTimeStep seconds
			RemainingTime = FMath::Min(MaxSimulationTimeStep, RemainingTime * 0.5f);
		}
		else
		{
			// If this is the last iteration, just use all the remaining time. This is usually better than cutting things short, as the simulation won't move far enough otherwise.
			// Print a throttled warning.
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			static uint32 s_WarningCount = 0;
			if ((s_WarningCount++ < 100) || (GFrameCounter & 15) == 0)
			{
				UE_LOG(LogProjectileMovement, Warning, TEXT("GetSimulationTimeStep() - Max iterations %d hit while remaining time %.6f > MaxSimulationTimeStep (%.3f) for '%s'"), MaxSimulationIterations, RemainingTime, MaxSimulationTimeStep, *GetPathNameSafe(UpdatedComponent));
			}
#endif
		}
	}

	// no less than MIN_TICK_TIME (to avoid potential divide-by-zero during simulation).
	return FMath::Max(MIN_TICK_TIME, RemainingTime);
}
void FBlueprintCompileReinstancer::ReinstanceFast()
{
	UE_LOG(LogBlueprint, Log, TEXT("BlueprintCompileReinstancer: Doing a fast path refresh on class '%s'."), *GetPathNameSafe(ClassToReinstance));

	TArray<UObject*> ObjectsToReplace;
	GetObjectsOfClass(DuplicatedClass, ObjectsToReplace, /*bIncludeDerivedClasses=*/ false);

	const bool bIsActor = ClassToReinstance->IsChildOf<AActor>();
	const bool bIsAnimInstance = ClassToReinstance->IsChildOf<UAnimInstance>();
	const bool bIsComponent = ClassToReinstance->IsChildOf<UActorComponent>();
	for (auto Obj : ObjectsToReplace)
	{
		UE_LOG(LogBlueprint, Log, TEXT("  Fast path is refreshing (not replacing) %s"), *Obj->GetFullName());

		if ((!Obj->IsTemplate() || bIsComponent) && !Obj->IsPendingKill())
		{
			const bool bIsSelected = bIsActor ? Obj->IsSelected() : false;

			Obj->SetClass(ClassToReinstance);

			if (bIsActor)
			{
				auto Actor = CastChecked<AActor>(Obj);
				Actor->ReregisterAllComponents();
				Actor->RerunConstructionScripts();

				if (bIsSelected)
				{
					GEditor->SelectActor(Actor, /*bInSelected =*/true, /*bNotify =*/true, false, true);
				}
			}

			if (bIsAnimInstance)
			{
				// Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a
				// paused world, need to initialise the skeletal mesh component that contains the anim instance.
				if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(Obj->GetOuter()))
				{
					SkelComponent->InitAnim(true);
				}
			}
		}
	}

	TArray<UObject*> SourceObjects;
	TMap<UObject*, UObject*> OldToNewInstanceMap;
	TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap;
	FReplaceReferenceHelper::IncludeCDO(DuplicatedClass, ClassToReinstance, OldToNewInstanceMap, SourceObjects, OriginalCDO);
	FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, &ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap);
}
uint32 FArchiveObjectCrc32::Crc32(UObject* Object, uint32 CRC)
{
#ifdef DEBUG_ARCHIVE_OBJECT_CRC32
	const double StartTime = FPlatformTime::Seconds();
	UE_LOG(LogArchiveObjectCrc32, Log, TEXT("### Calculating CRC for object: %s with outer: %s"), *Object->GetName(), Object->GetOuter() ? *Object->GetOuter()->GetName() : TEXT("NULL"));
#endif
	RootObject = Object;
	if (Object)
	{
		TSet<UObject*> SerializedObjects;

		// Start with the given object
		ObjectsToSerialize.Enqueue(Object);

		// Continue until we no longer have any objects to serialized
		while (ObjectsToSerialize.Dequeue(Object))
		{
			bool bAlreadyProcessed = false;
			SerializedObjects.Add(Object, &bAlreadyProcessed);
			// If we haven't already serialized this object
			if (!bAlreadyProcessed)
			{
#ifdef DEBUG_ARCHIVE_OBJECT_CRC32
				UE_LOG(LogArchiveObjectCrc32, Log, TEXT("- Serializing object: %s with outer: %s"), *Object->GetName(), Object->GetOuter() ? *Object->GetOuter()->GetName() : TEXT("NULL"));
#endif
				// Serialize it
				ObjectBeingSerialized = Object;
				if (!CustomSerialize(Object))
				{
					Object->Serialize(*this);
				}
				ObjectBeingSerialized = NULL;

				// Calculate the CRC, compounding it with the checksum calculated from the previous object
				CRC = FCrc::MemCrc32(SerializedObjectData.GetData(), SerializedObjectData.Num(), CRC);
#ifdef DEBUG_ARCHIVE_OBJECT_CRC32
				UE_LOG(LogArchiveObjectCrc32, Log, TEXT("=> object: '%s', total size: %d bytes, checksum: 0x%08x"), *GetPathNameSafe(Object), SerializedObjectData.Num(), CRC);
#endif
				// Cleanup
				MemoryWriter.Seek(0L);
				SerializedObjectData.Empty();
			}
		}

		// Cleanup
		SerializedObjects.Empty();
		RootObject = NULL;
	}

#ifdef DEBUG_ARCHIVE_OBJECT_CRC32
	UE_LOG(LogArchiveObjectCrc32, Log, TEXT("### Finished (%.02f ms), final checksum: 0x%08x"), (FPlatformTime::Seconds() - StartTime) * 1000.0f, CRC);
#endif
	return CRC;
}
void UGameplayCueManager::CheckForTooManyRPCs(FName FuncName, const FGameplayCuePendingExecute& PendingCue, const FString& CueID, const FGameplayEffectContext* EffectContext)
{
	if (GameplayCueCheckForTooManyRPCs)
	{
		static IConsoleVariable* MaxRPCPerNetUpdateCVar = IConsoleManager::Get().FindConsoleVariable(TEXT("net.MaxRPCPerNetUpdate"));
		if (MaxRPCPerNetUpdateCVar)
		{
			AActor* Owner = PendingCue.OwningComponent ? PendingCue.OwningComponent->GetOwner() : nullptr;
			UWorld* World = Owner ? Owner->GetWorld() : nullptr;
			UNetDriver* NetDriver = World ? World->GetNetDriver() : nullptr;
			if (NetDriver)
			{
				const int32 MaxRPCs = MaxRPCPerNetUpdateCVar->GetInt();
				for (UNetConnection* ClientConnection : NetDriver->ClientConnections)
				{
					if (ClientConnection)
					{
						UActorChannel** OwningActorChannelPtr = ClientConnection->ActorChannels.Find(Owner);
						TSharedRef<FObjectReplicator>* ComponentReplicatorPtr = (OwningActorChannelPtr && *OwningActorChannelPtr) ? (*OwningActorChannelPtr)->ReplicationMap.Find(PendingCue.OwningComponent) : nullptr;
						if (ComponentReplicatorPtr)
						{
							const TArray<FObjectReplicator::FRPCCallInfo>& RemoteFuncInfo = (*ComponentReplicatorPtr)->RemoteFuncInfo;
							for (const FObjectReplicator::FRPCCallInfo& CallInfo : RemoteFuncInfo)
							{
								if (CallInfo.FuncName == FuncName)
								{
									if (CallInfo.Calls > MaxRPCs)
									{
										const FString Instigator = EffectContext ? EffectContext->ToString() : TEXT("None");
										ABILITY_LOG(Warning, TEXT("Attempted to fire %s when no more RPCs are allowed this net update. Max:%d Cue:%s Instigator:%s Component:%s"), *FuncName.ToString(), MaxRPCs, *CueID, *Instigator, *GetPathNameSafe(PendingCue.OwningComponent));
									
										// Returning here to only log once per offending RPC.
										return;
									}

									break;
								}
							}
						}
					}
				}
			}
		}
	}
}
void UUserDefinedStructEditorData::RecreateDefaultInstance(FString* OutLog)
{
	UStruct* ScriptStruct = GetOwnerStruct();
	DefaultStructInstance.Recreate(ScriptStruct);
	uint8* StructData = DefaultStructInstance.GetStructMemory();
	ensure(DefaultStructInstance.IsValid() && DefaultStructInstance.GetStruct() == ScriptStruct);
	if (DefaultStructInstance.IsValid() && StructData && ScriptStruct)
	{
		DefaultStructInstance.SetPackage(ScriptStruct->GetOutermost());

		for (TFieldIterator<UProperty> It(ScriptStruct); It; ++It)
		{
			UProperty* Property = *It;
			if (Property)
			{
				auto VarDesc = VariablesDescriptions.FindByPredicate(FStructureEditorUtils::FFindByNameHelper<FStructVariableDescription>(Property->GetFName()));
				if (VarDesc && !VarDesc->CurrentDefaultValue.IsEmpty())
				{
					if (!FBlueprintEditorUtils::PropertyValueFromString(Property, VarDesc->CurrentDefaultValue, StructData))
					{
						const FString Message = FString::Printf(TEXT("Cannot parse value. Property: %s String: \"%s\" ")
							, (Property ? *Property->GetDisplayNameText().ToString() : TEXT("None"))
							, *VarDesc->CurrentDefaultValue);
						UE_LOG(LogClass, Warning, TEXT("UUserDefinedStructEditorData::RecreateDefaultInstance %s Struct: %s "), *Message, *GetPathNameSafe(ScriptStruct));
						if (OutLog)
						{
							OutLog->Append(Message);
						}
					}
				}
			}
		}
	}
}
Exemple #12
0
	void AddConvexElemsToRigidActor_AssumesLocked() const
	{
		float ContactOffsetFactor, MaxContactOffset;
		GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);

		for (int32 i = 0; i < BodySetup->AggGeom.ConvexElems.Num(); i++)
		{
			const FKConvexElem& ConvexElem = BodySetup->AggGeom.ConvexElems[i];

			if (ConvexElem.ConvexMesh)
			{
				PxTransform PLocalPose;
				bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose);

				PxConvexMeshGeometry PConvexGeom;
				PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh;
				PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs());
				FTransform ConvexTransform = ConvexElem.GetTransform();
				if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0)
				{
					UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(BodySetup->GetOuter()), i);
				}
				if (ConvexTransform.IsValid())
				{
					PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM);
					PLocalPose.q *= PElementTransform.q;
					PLocalPose.p = PElementTransform.p;
					PLocalPose.p.x *= Scale3D.X;
					PLocalPose.p.y *= Scale3D.Y;
					PLocalPose.p.z *= Scale3D.Z;

					if (PConvexGeom.isValid())
					{
						PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents();

						ensure(PLocalPose.isValid());
						{
							const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement());
							AttachShape_AssumesLocked(PConvexGeom, PLocalPose, ContactOffset);
						}
					}
					else
					{
						UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i);
					}
				}
				else
				{
					UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i);
				}
			}
			else
			{
				UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *BodySetup->GetPathName());
			}
		}
	}
Exemple #13
0
	void AddBoxesToRigidActor_AssumesLocked() const
	{
		float ContactOffsetFactor, MaxContactOffset;
		GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);

		for (int32 i = 0; i < BodySetup->AggGeom.BoxElems.Num(); i++)
		{
			const FKBoxElem& BoxElem = BodySetup->AggGeom.BoxElems[i];

			PxBoxGeometry PBoxGeom;
			PBoxGeom.halfExtents.x = (0.5f * BoxElem.X * Scale3DAbs.X);
			PBoxGeom.halfExtents.y = (0.5f * BoxElem.Y * Scale3DAbs.Y);
			PBoxGeom.halfExtents.z = (0.5f * BoxElem.Z * Scale3DAbs.Z);

			FTransform BoxTransform = BoxElem.GetTransform() * RelativeTM;
			if (PBoxGeom.isValid() && BoxTransform.IsValid())
			{
				PxTransform PLocalPose(U2PTransform(BoxTransform));
				PLocalPose.p.x *= Scale3D.X;
				PLocalPose.p.y *= Scale3D.Y;
				PLocalPose.p.z *= Scale3D.Z;

				ensure(PLocalPose.isValid());
				{
					const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoxGeom.halfExtents.minElement());
					AttachShape_AssumesLocked(PBoxGeom, PLocalPose, ContactOffset);
				}
			}
			else
			{
				UE_LOG(LogPhysics, Warning, TEXT("AddBoxesToRigidActor: [%s] BoxElems[%d] invalid or has invalid transform"), *GetPathNameSafe(BodySetup->GetOuter()), i);
			}
		}
	}
Exemple #14
0
	void AddSpheresToRigidActor_AssumesLocked() const
	{
		float ContactOffsetFactor, MaxContactOffset;
		GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);

		for (int32 i = 0; i < BodySetup->AggGeom.SphereElems.Num(); i++)
		{
			const FKSphereElem& SphereElem = BodySetup->AggGeom.SphereElems[i];

			PxSphereGeometry PSphereGeom;
			PSphereGeom.radius = (SphereElem.Radius * MinScaleAbs);

			if (ensure(PSphereGeom.isValid()))
			{
				FVector LocalOrigin = RelativeTM.TransformPosition(SphereElem.Center);
				PxTransform PLocalPose(U2PVector(LocalOrigin));
				PLocalPose.p *= MinScale;

				ensure(PLocalPose.isValid());
				{
					const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PSphereGeom.radius);
					AttachShape_AssumesLocked(PSphereGeom, PLocalPose, ContactOffset);
				}
			}
			else
			{
				UE_LOG(LogPhysics, Warning, TEXT("AddSpheresToRigidActor: [%s] SphereElem[%d] invalid"), *GetPathNameSafe(BodySetup->GetOuter()), i);
			}
		}
	}
void UK2Node_GenericCreateObject::EarlyValidation(class FCompilerResultsLog& MessageLog) const
{
	Super::EarlyValidation(MessageLog);
	auto ClassPin = GetClassPin(&Pins);
	const bool bAllowAbstract = ClassPin && ClassPin->LinkedTo.Num();
	auto ClassToSpawn = GetClassToSpawn();
	if (!UGameplayStatics::CanSpawnObjectOfClass(ClassToSpawn, bAllowAbstract))
	{
		MessageLog.Error(*FString::Printf(*LOCTEXT("GenericCreateObject_WrongClass", "Wrong class to spawn '%s' in @@").ToString(), *GetPathNameSafe(ClassToSpawn)), this);
	}

	auto OuterPin = GetOuterPin();
	if (!OuterPin || (!OuterPin->DefaultObject && !OuterPin->LinkedTo.Num()))
	{
		MessageLog.Error(*LOCTEXT("GenericCreateObject_NoOuter", "Outer object is required in @@").ToString(), this);
	}
}
UObject* FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter(UObject* Object, FName& OutName) const
{
	OutName = NAME_None;
	UObject* Outer = nullptr;

	UActorComponent* ActorComponent = Cast<UActorComponent>(Object);
	if (ActorComponent)
	{
		//if is child of a BPGC and not child of a CDO
		UBlueprintGeneratedClass* BPGC = nullptr;
		for (UObject* OuterObject = ActorComponent->GetOuter(); OuterObject && !BPGC; OuterObject = OuterObject->GetOuter())
		{
			if (OuterObject->HasAnyFlags(RF_ClassDefaultObject))
			{
				return Outer;
			}
			BPGC = Cast<UBlueprintGeneratedClass>(OuterObject);
		}

		for (UBlueprintGeneratedClass* SuperBPGC = BPGC; SuperBPGC && (OutName == NAME_None); SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperBPGC->GetSuperClass()))
		{
			if (SuperBPGC->InheritableComponentHandler)
			{
				FComponentKey FoundKey = SuperBPGC->InheritableComponentHandler->FindKey(ActorComponent);
				if (FoundKey.IsValid())
				{
					OutName = FoundKey.IsSCSKey() ? FoundKey.GetSCSVariableName() : ActorComponent->GetFName();
					Outer = BPGC->GetDefaultObject(false);
					break;
				}
			}
			if (SuperBPGC->SimpleConstructionScript)
			{
				for (auto Node : SuperBPGC->SimpleConstructionScript->GetAllNodes())
				{
					if (Node->ComponentTemplate == ActorComponent)
					{
						OutName = Node->VariableName;
						if (OutName != NAME_None)
						{
							Outer = BPGC->GetDefaultObject(false);
							break;
						}
					}
				}
			}
		}
	}

	if (Outer && (EReplacementResult::ReplaceCompletely == IsTargetedForReplacement(Object->GetClass())))
	{
		UE_LOG(LogBlueprintCodeGen, Log, TEXT("Object '%s' has replaced name '%s' and outer: '%s'"), *GetPathNameSafe(Object), *OutName.ToString(), *GetPathNameSafe(Outer));
		return Outer;
	}

	return nullptr;
}
EReplacementResult FBlueprintNativeCodeGenModule::IsTargetedForReplacement(const UObject* Object) const
{
	if (Object == nullptr)
	{
		return EReplacementResult::DontReplace;
	}

	const UStruct* Struct = Cast<UStruct>(Object);
	const UEnum* Enum = Cast<UEnum>(Object);

	if (Struct == nullptr && Enum == nullptr)
	{
		return EReplacementResult::DontReplace;
	}

	EReplacementResult Result = EReplacementResult::ReplaceCompletely;
	if (const UClass* BlueprintClass = Cast<UClass>(Struct))
	{
		if (UBlueprint* Blueprint = Cast<UBlueprint>(BlueprintClass->ClassGeneratedBy))
		{
			static const FBoolConfigValueHelper NativizeAnimBPOnlyWhenNonReducibleFuncitons(TEXT("BlueprintNativizationSettings"), TEXT("bNativizeAnimBPOnlyWhenNonReducibleFuncitons"));
			if (NativizeAnimBPOnlyWhenNonReducibleFuncitons)
			{
				if (UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint))
				{
					ensure(AnimBlueprint->bHasBeenRegenerated);
					if (AnimBlueprint->bHasAnyNonReducibleFunction == UBlueprint::EIsBPNonReducible::No)
					{
						UE_LOG(LogBlueprintCodeGen, Log, TEXT("AnimBP %s without non-reducible functions is excluded from nativization"), *GetPathNameSafe(Blueprint));
						Result = EReplacementResult::GenerateStub;
					}
				}
			}

			const EBlueprintType UnconvertableBlueprintTypes[] = {
				//BPTYPE_Const,		// WTF is a "const" Blueprint?
				BPTYPE_MacroLibrary,
				BPTYPE_LevelScript,
			};

			EBlueprintType BlueprintType = Blueprint->BlueprintType;
			for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(UnconvertableBlueprintTypes); ++TypeIndex)
			{
				if (BlueprintType == UnconvertableBlueprintTypes[TypeIndex])
				{
					Result = EReplacementResult::GenerateStub;
				}
			}

			static const FBoolConfigValueHelper DontNativizeDataOnlyBP(TEXT("BlueprintNativizationSettings"), TEXT("bDontNativizeDataOnlyBP"));
			if (DontNativizeDataOnlyBP)
			{
				if (FBlueprintEditorUtils::IsDataOnlyBlueprint(Blueprint))
				{
					return EReplacementResult::DontReplace;
				}
			}

			for (UBlueprintGeneratedClass* ParentClassIt = Cast<UBlueprintGeneratedClass>(BlueprintClass->GetSuperClass())
				; ParentClassIt; ParentClassIt = Cast<UBlueprintGeneratedClass>(ParentClassIt->GetSuperClass()))
			{
				EReplacementResult ParentResult = IsTargetedForReplacement(ParentClassIt);
				if (ParentResult != EReplacementResult::ReplaceCompletely)
				{
					Result = EReplacementResult::GenerateStub;
				}
			}

			for (TAssetSubclassOf<UBlueprint> ExcludedBlueprintTypeAsset : ExcludedBlueprintTypes)
			{
				UClass* ExcludedBPClass = ExcludedBlueprintTypeAsset.Get();
				if (!ExcludedBPClass)
				{
					ExcludedBPClass = ExcludedBlueprintTypeAsset.LoadSynchronous();
				}
				if (ExcludedBPClass && Blueprint->IsA(ExcludedBPClass))
				{
					Result = EReplacementResult::GenerateStub;
				}
			}
		}
	}

	auto IsObjectFromDeveloperPackage = [](const UObject* Obj) -> bool
	{
		return Obj && Obj->GetOutermost()->HasAllPackagesFlags(PKG_Developer);
	};

	auto IsDeveloperObject = [&](const UObject* Obj) -> bool
	{
		if (Obj)
		{
			if (IsObjectFromDeveloperPackage(Obj))
			{
				return true;
			}
			const UStruct* StructToTest = Obj->IsA<UStruct>() ? CastChecked<const UStruct>(Obj) : Obj->GetClass();
			for (; StructToTest; StructToTest = StructToTest->GetSuperStruct())
			{
				if (IsObjectFromDeveloperPackage(StructToTest))
				{
					return true;
				}
			}
		}
		return false;
	};

	if (Object && (IsEditorOnlyObject(Object) || IsDeveloperObject(Object)))
	{
		UE_LOG(LogBlueprintCodeGen, Warning, TEXT("Object %s depends on Editor or Development stuff. It shouldn't be cooked."), *GetPathNameSafe(Object));
		return EReplacementResult::DontReplace;
	}

	// check blacklists:
	// we can't use FindObject, because we may be converting a type while saving
	if (Enum && ExcludedAssetTypes.Find(Enum->GetPathName()) != INDEX_NONE)
	{
		Result = EReplacementResult::GenerateStub;
	}

	while (Struct)
	{
		if (ExcludedAssetTypes.Find(Struct->GetPathName()) != INDEX_NONE)
		{
			Result = EReplacementResult::GenerateStub;
		}
		Struct = Struct->GetSuperStruct();
	}

	if (ExcludedAssets.Contains(Object->GetOutermost()))
	{
		Result = EReplacementResult::GenerateStub;
	}

	return Result;
}
Exemple #18
0
void UBodySetup::AddSpheresToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, float MinScale, float MinScaleAbs, TArray<PxShape*>* NewShapes) const
{
	float ContactOffsetFactor, MaxContactOffset;
	GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);
	PxMaterial* PDefaultMat = GetDefaultPhysMaterial();

	for (int32 i = 0; i < AggGeom.SphereElems.Num(); i++)
	{
		const FKSphereElem& SphereElem = AggGeom.SphereElems[i];

		PxSphereGeometry PSphereGeom;
		PSphereGeom.radius = (SphereElem.Radius * MinScaleAbs);

		if (ensure(PSphereGeom.isValid()))
		{
			FVector LocalOrigin = RelativeTM.TransformPosition(SphereElem.Center);
			PxTransform PLocalPose(U2PVector(LocalOrigin));
			PLocalPose.p *= MinScale;

			ensure(PLocalPose.isValid());
			PxShape* NewShape = PDestActor->createShape(PSphereGeom, *PDefaultMat, PLocalPose);

			if (NewShapes)
			{
				NewShapes->Add(NewShape);
			}

			const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PSphereGeom.radius);
			NewShape->setContactOffset(ContactOffset);
		}
		else
		{
			UE_LOG(LogPhysics, Warning, TEXT("AddSpheresToRigidActor: [%s] SphereElem[%d] invalid"), *GetPathNameSafe(GetOuter()), i);
		}
	}
}
Exemple #19
0
void UBodySetup::AddBoxesToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, const FVector& Scale3D, const FVector& Scale3DAbs, TArray<PxShape*>* NewShapes) const
{
	float ContactOffsetFactor, MaxContactOffset;
	GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);
	PxMaterial* PDefaultMat = GetDefaultPhysMaterial();

	for (int32 i = 0; i < AggGeom.BoxElems.Num(); i++)
	{
		const FKBoxElem& BoxElem = AggGeom.BoxElems[i];

		PxBoxGeometry PBoxGeom;
		PBoxGeom.halfExtents.x = (0.5f * BoxElem.X * Scale3DAbs.X);
		PBoxGeom.halfExtents.y = (0.5f * BoxElem.Y * Scale3DAbs.Y);
		PBoxGeom.halfExtents.z = (0.5f * BoxElem.Z * Scale3DAbs.Z);

		FTransform BoxTransform = BoxElem.GetTransform() * RelativeTM;
		if (PBoxGeom.isValid() && BoxTransform.IsValid())
		{
			PxTransform PLocalPose(U2PTransform(BoxTransform));
			PLocalPose.p.x *= Scale3D.X;
			PLocalPose.p.y *= Scale3D.Y;
			PLocalPose.p.z *= Scale3D.Z;

			ensure(PLocalPose.isValid());
			PxShape* NewShape = PDestActor->createShape(PBoxGeom, *PDefaultMat, PLocalPose);

			if (NewShapes)
			{
				NewShapes->Add(NewShape);
			}

			const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoxGeom.halfExtents.minElement());
			NewShape->setContactOffset(ContactOffset);
		}
		else
		{
			UE_LOG(LogPhysics, Warning, TEXT("AddBoxesToRigidActor: [%s] BoxElems[%d] invalid or has invalid transform"), *GetPathNameSafe(GetOuter()), i);
		}
	}
}
Exemple #20
0
void UBodySetup::AddSphylsToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, const FVector& Scale3D, const FVector& Scale3DAbs, TArray<PxShape*>* NewShapes) const
{
	float ContactOffsetFactor, MaxContactOffset;
	GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);
	PxMaterial* PDefaultMat = GetDefaultPhysMaterial();

	float ScaleRadius = FMath::Max(Scale3DAbs.X, Scale3DAbs.Y);
	float ScaleLength = Scale3DAbs.Z;

	for (int32 i = 0; i < AggGeom.SphylElems.Num(); i++)
	{
		const FKSphylElem& SphylElem = AggGeom.SphylElems[i];

		// this is a bit confusing since radius and height is scaled
		// first apply the scale first 
		float Radius = FMath::Max(SphylElem.Radius * ScaleRadius, 0.1f);
		float Length = SphylElem.Length + SphylElem.Radius * 2.f;
		float HalfLength = Length * ScaleLength * 0.5f;
		Radius = FMath::Clamp(Radius, 0.1f, HalfLength);	//radius is capped by half length
		float HalfHeight = HalfLength - Radius;
		HalfHeight = FMath::Max(0.1f, HalfHeight);

		PxCapsuleGeometry PCapsuleGeom;
		PCapsuleGeom.halfHeight = HalfHeight;
		PCapsuleGeom.radius = Radius;

		if (PCapsuleGeom.isValid())
		{
			// The stored sphyl transform assumes the sphyl axis is down Z. In PhysX, it points down X, so we twiddle the matrix a bit here (swap X and Z and negate Y).
			PxTransform PLocalPose(U2PVector(RelativeTM.TransformPosition(SphylElem.Center)), U2PQuat(SphylElem.Orientation) * U2PSphylBasis);
			PLocalPose.p.x *= Scale3D.X;
			PLocalPose.p.y *= Scale3D.Y;
			PLocalPose.p.z *= Scale3D.Z;

			ensure(PLocalPose.isValid());
			PxShape* NewShape = PDestActor->createShape(PCapsuleGeom, *PDefaultMat, PLocalPose);

			if (NewShapes)
			{
				NewShapes->Add(NewShape);
			}

			const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PCapsuleGeom.radius);
			NewShape->setContactOffset(ContactOffset);
		}
		else
		{
			UE_LOG(LogPhysics, Warning, TEXT("AddSphylsToRigidActor: [%s] SphylElems[%d] invalid"), *GetPathNameSafe(GetOuter()), i);
		}
	}
}
bool FUniqueNetIdRepl::ImportTextItem(const TCHAR*& Buffer, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText)
{
	SetUniqueNetId(nullptr);

	bool bShouldWarn = true;
	if (Buffer)
	{
		static FString InvalidString(TEXT("INVALID"));
		if (Buffer[0] == TEXT('\0') || Buffer == InvalidString)
		{
			// An empty string or the word invalid are just considered expected invalid FUniqueNetIdRepls. No need to warn about those.
			bShouldWarn = false;
		}
		else
		{
			checkf(UOnlineEngineInterface::Get() && UOnlineEngineInterface::Get()->IsLoaded(), TEXT("Attempted to ImportText to FUniqueNetIdRepl while OSS is not loaded. Parent:%s"), *GetPathNameSafe(Parent));
			FString Contents(Buffer);
			UniqueIdFromString(Contents);
		}
	}

	if (bShouldWarn && !IsValid())
	{
#if !NO_LOGGING
		ErrorText->CategorizedLogf(LogNet.GetCategoryName(), ELogVerbosity::Warning, TEXT("Failed to import text to FUniqueNetIdRepl Parent:%s"), *GetPathNameSafe(Parent));
#endif
	}

	return true;
}
Exemple #22
0
void UBodySetup::AddConvexElemsToRigidActor(PxRigidActor* PDestActor, const FTransform& RelativeTM, const FVector& Scale3D, const FVector& Scale3DAbs, TArray<PxShape*>* NewShapes) const
{
	float ContactOffsetFactor, MaxContactOffset;
	GetContactOffsetParams(ContactOffsetFactor, MaxContactOffset);
	PxMaterial* PDefaultMat = GetDefaultPhysMaterial();

	for (int32 i = 0; i < AggGeom.ConvexElems.Num(); i++)
	{
		const FKConvexElem& ConvexElem = AggGeom.ConvexElems[i];

		if (ConvexElem.ConvexMesh)
		{
			PxTransform PLocalPose;
			bool bUseNegX = CalcMeshNegScaleCompensation(Scale3D, PLocalPose);

			PxConvexMeshGeometry PConvexGeom;
			PConvexGeom.convexMesh = bUseNegX ? ConvexElem.ConvexMeshNegX : ConvexElem.ConvexMesh;
			PConvexGeom.scale.scale = U2PVector(Scale3DAbs * ConvexElem.GetTransform().GetScale3D().GetAbs());
			FTransform ConvexTransform = ConvexElem.GetTransform();
			if (ConvexTransform.GetScale3D().X < 0 || ConvexTransform.GetScale3D().Y < 0 || ConvexTransform.GetScale3D().Z < 0)
			{
				UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has negative scale. Not currently supported"), *GetPathNameSafe(GetOuter()), i);
			}
			if (ConvexTransform.IsValid())
			{
				PxTransform PElementTransform = U2PTransform(ConvexTransform * RelativeTM);
				PLocalPose.q *= PElementTransform.q;
				PLocalPose.p = PElementTransform.p;
				PLocalPose.p.x *= Scale3D.X;
				PLocalPose.p.y *= Scale3D.Y;
				PLocalPose.p.z *= Scale3D.Z;

				if (PConvexGeom.isValid())
				{
					PxVec3 PBoundsExtents = PConvexGeom.convexMesh->getLocalBounds().getExtents();

					ensure(PLocalPose.isValid());
					PxShape* NewShape = PDestActor->createShape(PConvexGeom, *PDefaultMat, PLocalPose);

					if (NewShapes)
					{
						NewShapes->Add(NewShape);
					}

					const float ContactOffset = FMath::Min(MaxContactOffset, ContactOffsetFactor * PBoundsExtents.minElement());
					NewShape->setContactOffset(ContactOffset);
				}
				else
				{
					UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] invalid"), *GetPathNameSafe(GetOuter()), i);
				}
			}
			else
			{
				UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: [%s] ConvexElem[%d] has invalid transform"), *GetPathNameSafe(GetOuter()), i);
			}
		}
		else
		{
			UE_LOG(LogPhysics, Warning, TEXT("AddConvexElemsToRigidActor: ConvexElem is missing ConvexMesh (%d: %s)"), i, *GetPathName());
		}
	}
}
void FBlueprintCompileReinstancer::ReinstanceInner(bool bForceAlwaysReinstance)
{
	if (ClassToReinstance && DuplicatedClass)
	{
		static const FBoolConfigValueHelper ReinstanceOnlyWhenNecessary(TEXT("Kismet"), TEXT("bReinstanceOnlyWhenNecessary"), GEngineIni);
		bool bShouldReinstance = true;
		// See if we need to do a full reinstance or can do the faster refresh path (when enabled or no values were modified, and the structures match)
		if (ReinstanceOnlyWhenNecessary && !bForceAlwaysReinstance)
		{
			BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplaceClassNoReinsancing);

			const UBlueprintGeneratedClass* BPClassA = Cast<const UBlueprintGeneratedClass>(DuplicatedClass);
			const UBlueprintGeneratedClass* BPClassB = Cast<const UBlueprintGeneratedClass>(ClassToReinstance);
			const UBlueprint* BP = Cast<const UBlueprint>(ClassToReinstance->ClassGeneratedBy);

			static const FBoolConfigValueHelper ChangeDefaultValueWithoutReinstancing(TEXT("Kismet"), TEXT("bChangeDefaultValueWithoutReinstancing"), GEngineIni);
			const bool bTheSameDefaultValues = (BP != nullptr) && (ClassToReinstanceDefaultValuesCRC != 0) && (BP->CrcPreviousCompiledCDO == ClassToReinstanceDefaultValuesCRC);

			const bool bTheSameLayout = (BPClassA != nullptr) && (BPClassB != nullptr) && FStructUtils::TheSameLayout(BPClassA, BPClassB, true);

			const bool bAllowedToDoFastPath = (ChangeDefaultValueWithoutReinstancing || bTheSameDefaultValues) && bTheSameLayout;
			if (bAllowedToDoFastPath)
			{
				ReinstanceFast();
				bShouldReinstance = false;
			}
		}

		if (bShouldReinstance)
		{
			UE_LOG(LogBlueprint, Log, TEXT("BlueprintCompileReinstancer: Doing a full reinstance on class '%s'"), *GetPathNameSafe(ClassToReinstance));
			ReplaceInstancesOfClass(DuplicatedClass, ClassToReinstance, OriginalCDO, &ObjectsThatShouldUseOldStuff);
		}
		else if (ClassToReinstance->IsChildOf<UActorComponent>())
		{
			// ReplaceInstancesOfClass() handles this itself, if we had to re-instance
			ReconstructOwnerInstances(ClassToReinstance);
		}
	}
}
void UTimelineTemplate::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);

	UObject* NewCurveOuter = GetOuter();

#if WITH_EDITOR
	// The outer should always be a BlueprintGeneratedClass, however the Curves get put into the Blueprint, however if they belong to the Transient package, that's where the duplicated Blueprint should be.
	if(GetOutermost() != GetTransientPackage())
	{
		if(UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(GetOuter()))
		{
			NewCurveOuter = BPClass->ClassGeneratedBy;
		}
	}
#endif // WITH_EDITOR

	for(TArray<struct FTTFloatTrack>::TIterator It = FloatTracks.CreateIterator();It;++It)
	{
		FTTFloatTrack& Track = *It;
		if( Track.CurveFloat != NULL )
		{
			// Do not duplicate external curves unless duplicating to a transient package
			if(!Track.CurveFloat->GetOuter()->IsA(UPackage::StaticClass()) || GetOutermost() == GetTransientPackage())
			{
				Track.CurveFloat = DuplicateObject<UCurveFloat>(Track.CurveFloat, NewCurveOuter, *MakeUniqueCurveName(Track.CurveFloat, NewCurveOuter));
			}
		}
		else
		{
			UE_LOG(LogBlueprint, Warning, TEXT("Timeline %s Track %s in %s has an invalid curve.  Please fix!"), *TimelineTemplateNameToVariableName(GetFName()), *Track.TrackName.ToString(), *GetPathNameSafe(GetOuter()));
		}
	}

	for(TArray<struct FTTEventTrack>::TIterator It = EventTracks.CreateIterator();It;++It)
	{
		FTTEventTrack& Track = *It;
		if( Track.CurveKeys != NULL )
		{
			// Do not duplicate external curves unless duplicating to a transient package
			if(!Track.CurveKeys->GetOuter()->IsA(UPackage::StaticClass()) || GetOutermost() == GetTransientPackage())
			{
				Track.CurveKeys = DuplicateObject<UCurveFloat>(Track.CurveKeys, NewCurveOuter, *MakeUniqueCurveName(Track.CurveKeys, NewCurveOuter));
			}
		}
		else
		{
			UE_LOG(LogBlueprint, Warning, TEXT("Timeline %s Track %s in %s has an invalid curve.  Please fix!"), *TimelineTemplateNameToVariableName(GetFName()), *Track.TrackName.ToString(), *GetPathNameSafe(GetOuter()));
		}
	}

	for(TArray<struct FTTVectorTrack>::TIterator It = VectorTracks.CreateIterator();It;++It)
	{
		FTTVectorTrack& Track = *It;
		if( Track.CurveVector != NULL )
		{
			// Do not duplicate external curves unless duplicating to a transient package
			if(!Track.CurveVector->GetOuter()->IsA(UPackage::StaticClass()) || GetOutermost() == GetTransientPackage())
			{
				Track.CurveVector = DuplicateObject<UCurveVector>(Track.CurveVector, NewCurveOuter, *MakeUniqueCurveName(Track.CurveVector, NewCurveOuter));
			}
		}
		else
		{
			UE_LOG(LogBlueprint, Warning, TEXT("Timeline %s Track %s in %s has an invalid curve.  Please fix!"), *TimelineTemplateNameToVariableName(GetFName()), *Track.TrackName.ToString(), *GetPathNameSafe(GetOuter()));
		}
	}

	for(TArray<struct FTTLinearColorTrack>::TIterator It = LinearColorTracks.CreateIterator();It;++It)
	{
		FTTLinearColorTrack& Track = *It;
		if( Track.CurveLinearColor != NULL )
		{
			// Do not duplicate external curves unless duplicating to a transient package
			if(!Track.CurveLinearColor->GetOuter()->IsA(UPackage::StaticClass()) || GetOutermost() == GetTransientPackage())
			{
				Track.CurveLinearColor = DuplicateObject<UCurveLinearColor>(Track.CurveLinearColor, NewCurveOuter, *MakeUniqueCurveName(Track.CurveLinearColor, NewCurveOuter));
			}
		}
		else
		{
			UE_LOG(LogBlueprint, Warning, TEXT("Timeline %s Track %s in %s has an invalid curve.  Please fix!"), *TimelineTemplateNameToVariableName(GetFName()), *Track.TrackName.ToString(), *GetPathNameSafe(GetOuter()));
		}
	}

	TimelineGuid = FGuid::NewGuid();
}