You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3194900 on 2016/11/11 by Ryan.Rauschkolb
Fixed issue where Reroute node pins weren't mirroring data properly.
#jira UE-33717
Change 3195081 on 2016/11/11 by Dan.Oconnor
This @todo was addressed
Change 3196368 on 2016/11/14 by Maciej.Mroz
Results of FBlueprintNativeCodeGenModule::IsTargetedForReplacement are cashed - optimization (cooking time).
Change 3196369 on 2016/11/14 by Maciej.Mroz
CompileDisplaysBinaryBackend, CompileDisplaysTextBackend and bDisplaysLayout features (in [Kismet] in Engine.ini) are disabled in commandlets. They slow down BP compilation.
Change 3196398 on 2016/11/14 by Ben.Cosh
Reworked the tracking of latent and expansion event tracking in the blueprint compiler for use with the blueprint profiler.
#Jira - UE-37364 - Crash: PIE with instrumented PlayerPawn_Generic added to AITestbed scene
#Proj BlueprintProfiler, KismetCompiler. BlueprintGraph, Engine
Change 3196410 on 2016/11/14 by Maciej.Mroz
Fixed crash in UK2Node_Knot::PropagatePinTypeFromInput
Change 3196852 on 2016/11/14 by Maciej.Mroz
Fixed static analysis warning.
Change 3196874 on 2016/11/14 by Maciej.Mroz
#jira UE-37778
(the issue was already fixed, but it was reintroduced, when EDL support was added).
ObjectImport->XObject is not filled prematurelly, so CreateExport is properly called each dynamic class.
Change 3197469 on 2016/11/14 by Dan.Oconnor
Fix for being able to make Sets and Maps of user defined structs that contained unhashable types (e.g. Rotator)
Change 3197703 on 2016/11/14 by Dan.Oconnor
Updated documentation comment to reflect current behavior
Change 3198167 on 2016/11/15 by Maciej.Mroz
Merged 3196582 from Dev-Core
UE4 - Changed a check to a warning related to detaching linekrs twice. Seen in nativized BP version of platformer game.
Change 3198433 on 2016/11/15 by Ryan.Rauschkolb
Fixed Copy/pasting variable nodes hides them from a reference search
#UE-31606
Change 3198811 on 2016/11/15 by Maciej.Mroz
Fixed Knot node - it will use/propagate the type from input connection, if it's possible (intstead of the type from output connection).
Change 3198866 on 2016/11/15 by Maciej.Mroz
#jira UE-38578
Fixed infinite loading of DynamicClass in EDL.
Change 3199045 on 2016/11/15 by Phillip.Kavan
[UE-27402] Fix attached Actor-based Blueprint instance root component relative transform values after reconstruction.
change summary:
- modified FActorComponentInstanceData's ctor to exclude relative transform properties when caching root component values.
#jira UE-27402
Change 3200703 on 2016/11/16 by Mike.Beach
Marking the ease node explicitly as pure, which makes it so we can prune it from graphs when it is unused.
#jira UE-38453
Change 3201115 on 2016/11/16 by Maciej.Mroz
Nativization + EDL: "Dynamic" objects are processed by FAsyncPackage::PostLoadDeferredObjects, so the EInternalObjectFlags::AsyncLoading flag is properly cleared.
Change 3201749 on 2016/11/17 by Maciej.Mroz
In EDL a package containig a dynamic class has PKG_CompiledIn flag (the same like without EDL).
Change 3202577 on 2016/11/17 by Mike.Beach
Accounting for a change in our intermediate node mapping - the old list no longer maps expanded nodes to macro instances (instead it maps to the corresponding node in the macro graph), so we had to use a new mapping meant for this.
#jira UE-35609
Change 3204803 on 2016/11/18 by Phillip.Kavan
[UE-38607] Implicitly turn on Blueprint class nativization for dependencies.
change summary:
- added a UBlueprint::PostEditChangeProperty() override method to handle this.
#jira UE-38607
Change 3204812 on 2016/11/18 by Phillip.Kavan
[UE-38580] Implicitly turn on the "nativize" project setting when enabling nativize for any Blueprint class.
change summary:
- modified UBlueprint::PostEditChangeProperty() to update project packaging settings if necessary
#jira UE-38580
Change 3204818 on 2016/11/18 by Phillip.Kavan
[UE-38725] Interface class dependencies that are not enabled for nativization will now raise an error during nativized cooks.
change summary:
- modified FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to check interface class dependencies in addition to parent class dependencies.
#jira UE-38725
Change 3204963 on 2016/11/18 by Dan.Oconnor
Create a transaction when using UBlueprintFunctionNodeSpawner* directly
#jira UE-36439
Change 3206510 on 2016/11/21 by Mike.Beach
Adding math-expression aliases for dot and cross functions ("dot" and "cross" respectively).
#jira UEBP-71
Change 3206547 on 2016/11/21 by Mike.Beach
Exposing GetReflectionVector() to Blueprints.
#jira UEBP-70
Change 3206658 on 2016/11/21 by Dan.Oconnor
Fix for compile error, digging out authorative class from trash class.
Mirror of 3206638 from Odin
#jira None
Change 3207579 on 2016/11/22 by Mike.Beach
No longer enforcing the requirement that game UFunctions have to have a category (making writing of game code easier).
#jira UE-18093
Change 3207956 on 2016/11/22 by Phillip.Kavan
[UE-38690] Fix a regression in which nested scene component subobjects would no longer be registered after construction of an instance-added component in IWCE.
change summary:
- modified the IWCE path in SSCSEditor::AddNewComponent() to ensure that any components added as a result of instancing the newly-added component are also registered.
- modified AActor::ExecuteConstruction() to ensure that non-scene, native nested component subobjects that might be created as a result of SCS execution are also registered (previously it was only considering non-scene components that were explicitly created by the SCS, or that inherited the creation method of the instance that created it).
#jira UE-38690
Change 3208217 on 2016/11/22 by Mike.Beach
Modified fix (originally from Ryan.Rauschkolb, CL 3186023):
Fixed unable to set struct variable name if name includes space
#jira UE-28435
Change 3208347 on 2016/11/22 by Mike.Beach
Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints)
Change 3208688 on 2016/11/23 by Ben.Cosh
Made a minor change that forces debugging references to the PIE world when the play in editor world is changed. This is intended to better handle mutliple game instance/world debugging scenarios.
#Jira UE-26386 - Can't hit breakpoints in blueprints for level script for server instances
#Proj Engine, UnrealEd
Change 3208712 on 2016/11/23 by Ben.Cosh
Improved handling of unwired transform struct terminal expression's in the blueprint VM compiler to remove an errant warning.
#Jira UE-32401 - "ImportText: Missing opening parenthesis" message, when a function returns Transform
#Proj KismetCompiler
Change 3209457 on 2016/11/23 by Phillip.Kavan
[UE-30479] Fix inability to edit the ISMC instance array on Actor instances when the ISMC is inherited from a Blueprint class.
change summary:
- added PPF_ForceTaggedSerialization as a means to override the CPF_SkipSerialization flag when explicit serialization of the property value is needed
- modified UProperty::ShouldSerializeValue() to check for and handle the PPF_ForceTaggedSerialization flag when the CPF_SkipSerialization flag is present
- modified UActorComponent::DetermineUCSModifiedProperties() to add the PPF_ForceTaggedSerialization flag to the custom FArchive impl
- modified FActorComponentInstanceData::FActorComponentInstanceData() to add the PPF_ForceTaggedSerialization flag to the custom FObjectWriter impl
- modified FActorComponentInstanceData::ApplyToComponent() to add the PPF_ForceTaggedSerialization flag to the custom FObjectReader impl
#jira UE-30479
Change 3209758 on 2016/11/24 by Maciej.Mroz
#jira UE-38979
Nativization: fixed error when a BP implements a native interface.
FBlueprintNativeCodeGenModule::IsTargetedForReplacement will return "DontReplace" for native class.
Change 3210376 on 2016/11/25 by Maciej.Mroz
#jira UE-39028
Fixed FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter
Components in nativized BPCG SCS have replaced outer object and name while cooking.
Change 3210936 on 2016/11/28 by Phillip.Kavan
Minor revision to try and avoid a potentially expensive Contains() call when possible.
Change 3211527 on 2016/11/28 by Maciej.Mroz
Fixed map of names cooked in packages in nativized build.
Change 3211969 on 2016/11/28 by Mike.Beach
Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints)
Change 3212328 on 2016/11/28 by Dan.Oconnor
Enum, pointer and arithmetic specializations for THasGetTypeHash, as VC doesn't detect them properly.
TIsEnum moved to its own header. Submitted on behalf of steve.robb
Change 3212398 on 2016/11/28 by Dan.Oconnor
Build fix, this function is part of another change
Change 3212442 on 2016/11/28 by Dan.Oconnor
UHT now supports structs in TMap and TSet, misc. fixes to PropertyStruct's PropertyFlags detecting whether the struct type is hashable (all HasGetTypeHash flags are now computed from THasGetTypeHash). Various fixes for generating TMap/TSet code from blueprints
Change 3212578 on 2016/11/28 by Dan.Oconnor
Rename RegisterClass to avoid collsion with RegistClass macro in generated code
Change 3213668 on 2016/11/29 by Dan.Oconnor
Fix for missing CDO when instatiating some subobjects in nativized BPs
#jira UE-34980
Change 3213737 on 2016/11/29 by Dan.Oconnor
Added GetTypeHash implementation to UEnumProperty
#jira UE-39091
Change 3215225 on 2016/11/30 by Maciej.Mroz
Bunch of changes required for nativized Orion to work with EDL.
- ClientOnly, ServerOnly and EditorOnly assets are properly distinguished and handled
- Introduced FCompilerNativizationOptions
- fixed inproper references to BP instead of BPGC
- fixed generated delegate name
- hack for DefaultRootNode UE-39168
- improved NativeCodeGenrationTool
- various minor improvements
Change 3216363 on 2016/11/30 by Dan.Oconnor
Better fix for discrepency between UUserDefinedEnum::GetEnumText and UEnum::GetEnumText. Without meta data we could not look up display names, so I'm writing out the display names into a function in the BP cpp backend. This function could be generated by UHT if we wanted to correct this odd behavior for native enums
#jira UE-27735
Change 3217168 on 2016/12/01 by Maciej.Mroz
#jira UE-35390
Nativization:
Fixed compilation warning C4458: declaration of 'CurrentState' hides class member
Disabled warning C4996 (deprecated) in nativized code.
Change 3217320 on 2016/12/01 by Phillip.Kavan
[UE-38652] Selecting Blueprint assets for nativization is now integrated into the project's configuration.
change summary:
- added EProjectPackagingBlueprintNativizationMethod
- deprecated the 'bNativizeBlueprintAssets' and 'bNativizeOnlySelectedBlueprints' flags in favor of a new 'BlueprintNativizationMethod' config property in UProjectPackagingSettings
- added a new 'NativizeBlueprintAssets' config property to UProjectPackagingSettings to track/store the list of Blueprints to be nativized when the exclusive method (whitelist) is selected
- added a PostInitProperties() override to UProjectPackagingSettings; implemented to migrate from the deprecated config properties to the new method enum type
- updated FMainFrameActionCallbacks::PackageProject() to migrate to checking the enum type
- updated FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to migrate to checking the enum type
- modified UProjectPackagingSettings::CanEditChange() to enable editing of the nativization whitelist only when the "exclusive" method is active
- modified UProjectPackagingSettings::PostEditChangeProperty() to add a new case for handling changes to the whitelist; changes are propagated to any Blueprint assets that are loaded
- added new Add/RemoveBlueprintAssetFromNativizationList() APIs to UProjectPackagingSettings for assisting with adding/removing Blueprint assets to/from the exclusive list (whitelist)
- deprecated the 'bNativize' flag in UBlueprint in favor of a new transient 'bSelectedForNativization' flag that is no longer serialized; this now caches whether or not the asset is present in the whitelist in the project config
- modified UBlueprint::Serialize() to both migrate from the deprecated flag to the project config/transient flag on load, as well as to propagate the value of the transient flag back to the project config on save. this means that if the user changes the value of the transient flag through the Details view, that change won't be reflected back to the project config until the Blueprint is actually saved (saving the value to the config rather than serializing to the asset)
- modified UBlueprint::PostEditChangeProperty() to remove code that was previously updating the project configuration at edit time. this functionality has been relocated to Serialize() (save time) instead.
notes:
- also completes UE-38636 (task: consolidate config options into a single drop-down)
#jira UE-38652
Change 3218124 on 2016/12/01 by Dan.Oconnor
CPF_HasGetValueTypeHash flag was not set on native UEnumProperties
#jira UE-39181
Change 3218168 on 2016/12/01 by Phillip.Kavan
Fix local var name that shadows a function param (CIS fix).
Change 3219117 on 2016/12/02 by Maciej.Mroz
#jira UE-39241 "warning C4458: declaration of XXX hides class member" In Nativized Code
Nativization:
Fixed compilation warning C4458: when local function variable hides a class variable. Names of local variables in funvtions have prefix "bpfv__".
Change 3219201 on 2016/12/02 by Mike.Beach
Keeping the "Select All Input Nodes" option from infinitely recurssing by blocking on nodes it has already selected.
#jira UE-38988
Change 3219247 on 2016/12/02 by Mike.Beach
Fixing CIS shadow variable warning from my last check in (CL 3219201).
Change 3219332 on 2016/12/02 by Maciej.Mroz
#jira HeaderParser: "private:" specifier is lost in FGameplayTag::TagName
Workaround for UE-38231
Change 3219381 on 2016/12/02 by Mike.Beach
Accounting for cyclic compile issues in cast-node's validate function, making it check the authoratative class instead of the current type. Also down grading some of the warnings to notes (suggesting the users don't need the cast).
#jira UE-39272
Change 3220224 on 2016/12/02 by Dan.Oconnor
Reduce font size for compact nodes
Change 3220476 on 2016/12/03 by Maciej.Mroz
#jira UE-35390
Better fix for UE-35390
Disabled deprecation warnings in nativized code.
Change 3221637 on 2016/12/05 by Maciej.Mroz
#jira UEBP-245
Nativization:
Forced ImportCreation while InitialLoad for DynamicClasses.
Change 3222306 on 2016/12/05 by Dan.Oconnor
Support for default values of TMap and TSet local variables
#jira UE-39239
Change 3222383 on 2016/12/05 by Dan.Oconnor
Fixed bug in Blueprint TMap function for getting values out of a TMap
Change 3222427 on 2016/12/05 by Mike.Beach
Preventing ChildActorTemplate fixups from occuring on component load, when they may instead be a placeholder object (a byproduct of deferred Blueprint loading - a guard against cyclic load problems).
#jira UE-39323
Change 3222679 on 2016/12/05 by Dan.Oconnor
Remove unused code. Had no sideeffects, other than potential for leak when struct with non-trivial dtor was allocated here.
Change 3222719 on 2016/12/05 by Dan.Oconnor
Earlier detection of invalid native map/set properties. These generate a compile error if any TMap/TSet functions are used, but will slip by undetected if not used. Working on static assert to catch them.
#jira UE-39338
Change 3224375 on 2016/12/06 by Dan.Oconnor
Add tags for testing of array diffing
Change 3224507 on 2016/12/07 by Phillip.Kavan
[UE-39055] Fix a crash caused by an object name collision that could occur when loading some older Blueprint assets.
change summary:
- added UInheritableComponentHandler::FixComponentTemplateName()
- modified UInheritableComponentHandler::PostLoad() to check for and correct stale records that cause a collision with the corrected name
- added a UInheritableComponentHandler::Serialize() override to ensure that UsingCustomVersion() is called for the asset containing the ICH (already happening in UBlueprint::Serialize(), but added for consistency with other usage)
- modified USimpleConstructionScript::Serialize() to ensure that UsingCustomVersion() is called for the asset containing the SCS (same reason as above)
#jira UE-39055
Change 3225572 on 2016/12/07 by Samuel.Proctor
Test assets for TSet/TMap testing. Includes new native class for testing containers. #rb none
Change 3225577 on 2016/12/07 by Samuel.Proctor
New test map for Array, TSet and Tmap testing.
Change 3226281 on 2016/12/07 by Dan.Oconnor
Container test asset, needs to be in p4 for diff tool tests.
Change 3226345 on 2016/12/07 by Dan.Oconnor
Another revision of test data
Change 3228496 on 2016/12/09 by Ben.Cosh
This change adds extra information to component template arrays so that the component class can be determined in builds that strip out objects of certain class types such as the editor dedicated server build.
#Jira UE-38842 - "LogBlueprint:Error: [Compiler BP_Skybox_World_RandomTrees_01] Error Can't connect pins ReturnValue and Target" after entering a lobby in a synced server
#Proj KismetCompiler, BlueprintGraph, UnrealEd, Core, Engine, Kismet, BlueprintCompilerCppBackend
Change 3230120 on 2016/12/09 by Dan.Oconnor
Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints)
Change 3230700 on 2016/12/12 by Samuel.Proctor
Removing some array test properties from container test class that were not needed. Also updated struct element to better reflect testing purpose. #rb none
Change 3230926 on 2016/12/12 by Samuel.Proctor
Missed a file on previous container test native QA asset checkin. #rb none
Change 3231246 on 2016/12/12 by Dan.Oconnor
PR #3003: New Feature: In-editor diff of arrays and structs now highlights diff. (Contributed by CA-ADuran).
I've added TSet and TMap support as well.
Change 3231311 on 2016/12/12 by Dan.Oconnor
Handle class load failure
#jira UE-39480
Change 3231387 on 2016/12/12 by Dan.Oconnor
Shadow variable fixes
Change 3231501 on 2016/12/12 by Dan.Oconnor
More shadow fixes
Change 3231584 on 2016/12/12 by Maciej.Mroz
#jira UE-39636
Replaced obsolate macro usage.
#fyi Dan.Oconnor
Change 3231685 on 2016/12/12 by Mike.Beach
PR #3032: Fixed category for IsValidIndex (Contributed by elFarto)
#jira UE-39660
Change 3231689 on 2016/12/12 by Maciej.Mroz
Nativization: Fixed Dependency list generation.
Change 3231765 on 2016/12/12 by Phillip.Kavan
[UE-38903] Auto-enable exclusive Blueprint nativization only after explicitly selecting the first asset.
change summary:
- fixed up the auto-enable logic on save in UBlueprint::Serialize()
#jira UE-38903
#fyi Maciej.Mroz
Change 3231837 on 2016/12/12 by Dan.Oconnor
Restore hack to keep objects referenced by bytecode alive while in editor
#jira UE-38486
Change 3232085 on 2016/12/13 by Phillip.Kavan
Compile fix.
Change 3232435 on 2016/12/13 by Ben.Cosh
Fix for a bug introduced in CL 3228496 that caused component templates to fail to be identified by name and resulted in blueprint compilation issues for add component nodes.
#Jira UE-39623 - Unknown template referenced by Add Component Node
#Proj BlueprintGraph, Engine
Change 3232437 on 2016/12/13 by Maciej.Mroz
#jira UE-33021
Remove an obsolete warning.
Change 3232564 on 2016/12/13 by Ben.Cosh
This adds extra component template renaming propagation and checking for the blueprint generated class and blueprint skeleton class.
#Jira UE-39623 - Unknown template referenced by Add Component Node
#Proj BlueprintGraph
- Implementing a bit of review feedback and some safety checking.
Change 3232598 on 2016/12/13 by Maciej.Mroz
Nativization:
stati functions cannot be const, so no workaound (_Inner function) specyfic to const functions is necessary
#jira UE-39518
Change 3232601 on 2016/12/13 by Phillip.Kavan
[UE-38975] Warn on BuildCookRun or a standalone cook when the -nativizeAssets flag is omitted from the command line for a nativization-enabled project.
change summary:
- added 'bWarnIfPackagedWithoutNativizationFlag' to UProjectPackagingSettings (default = true)
- modified UCookOnTheFlyServer::StartCookByTheBook() to check for the presence of the -nativizeAssets flag and emit a warning for unexpected omission from the command line
- modified UAT to include a warning for the BuildCookRun command when -build is specified with the same unexpected omission of the -nativizeAssets flag on the UAT command line
#jira UE-38975
Change 3232749 on 2016/12/13 by Mike.Beach
Moving Blueprint nativization out of the experimental category.
#jira UE-39358
Change 3233043 on 2016/12/13 by Dan.Oconnor
Various fixes for TSet/TMap nativization issues
#jira UE-39634
Change 3233086 on 2016/12/13 by Dan.Oconnor
Advanced Containers (TSet/TMap) no longer experimental
Change 3233175 on 2016/12/13 by Mike.Beach
Whitelising "Packaging" as an acceptable BP settings category (follow up to CL 3232749).
#jira UE-39358
Change 3233182 on 2016/12/13 by Mike.Beach
Exposing the editor setting "Show Action Menu Item Signatures" through the Blueprint Developer menu (for ease of access).
Change 3233662 on 2016/12/13 by Phillip.Kavan
[UE-39722] Fix a typo that led to a UAT runtime failure.
#jira UE-39722
Change 3233710 on 2016/12/13 by Dan.Oconnor
Clang template useage fix
#jira UE-39742
Change 3233895 on 2016/12/13 by Dan.Oconnor
Several fixes to crashes that occur when you delete a blueprint asset when its children are not loaded.
#jira UE-39558
Change 3234443 on 2016/12/14 by Phillip.Kavan
[UE-39354] Fix script VM crash on assignment to TSet/TMap variables.
change summary:
- modified FScriptBuilderBase::EmitDestinationExpression() to consider TSet/TMap value types in addition to TArray terms
#jira UE-39354
Change 3234581 on 2016/12/14 by Mike.Beach
Backing out fix for UE-38842 (CL 3228496/3232435/3232564) - mapping UBlueprintGeneratedClass's ComponentTemplates array to a new format was causing issues with deferred dependency loading during serialization (trying to extract type information from a placeholder object). We're opting for a smaller/simpler solution to UE-38842, which will be to store the component information on the node itself (not with the templates).
#jira UE-39707
Change 3234729 on 2016/12/14 by Mike.Beach
Making it so AddComponent nodes now track the component (class) type that they represent (in case the template cannot be spawned, like in -server w/ client-only components).
#jira UE-38842
Change 3234805 on 2016/12/14 by Mike.Beach
Fixing CIS shadowed variable warning.
Change 3234830 on 2016/12/14 by Nick.Atamas
Added extra debugging mechanisms to help track down duplicate item issues with TableViews.
Change 3235075 on 2016/12/14 by Mike.Beach
Creating a helper to better manage nested scope blocks added in generated code - on close, clears out cached local accessor variables that were added, so we don't use one that was declared inside the nested scope.
#jira UE-39769
Change 3235213 on 2016/12/14 by Phillip.Kavan
[UE-39790] Fix UAT compile issue after latest merge from Main.
change summary:
- migrated the BuildCookRun command's usage of the (deprecated) ConfigCacheIni to the new ConfigHierarchy API
#jira UE-39790
Change 3235384 on 2016/12/14 by Mike.Beach
Defaulting to excluding data-only Blueprints from nativization.
Change 3235675 on 2016/12/14 by Nick.Atamas
Hopefully fixed build.
Added OnEnteredBadState delegate that lets users add arbitrary logging info when the List/Tree enters a bad state.
Change 3235761 on 2016/12/14 by Mike.Beach
Hopefully resolving CIS mac/ps4 build failures in Dev-BP for 4.15 integration.
#jira UE-39800
Change 3235800 on 2016/12/14 by Mike.Beach
More hopeful CIS mac/ps4 fixes for 4.15 integration.
#jira UE-39800
[CL 3236017 by Mike Beach in Main branch]
1774 lines
70 KiB
C++
1774 lines
70 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Misc/Guid.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/Package.h"
|
|
#include "UObject/StructOnScope.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "UObject/UObjectHash.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "Components/SceneComponent.h"
|
|
#include "Engine/LatentActionManager.h"
|
|
#include "PhysicsEngine/BodyInstance.h"
|
|
#include "Components/PrimitiveComponent.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "Engine/TimelineTemplate.h"
|
|
#include "Engine/UserDefinedEnum.h"
|
|
#include "Engine/UserDefinedStruct.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "KismetCompiler.h"
|
|
#include "BlueprintCompilerCppBackend.h"
|
|
#include "BlueprintCompilerCppBackendGatherDependencies.h"
|
|
#include "IBlueprintCompilerCppBackendModule.h"
|
|
#include "BlueprintCompilerCppBackendUtils.h"
|
|
#include "Kismet2/StructureEditorUtils.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "Engine/InheritableComponentHandler.h"
|
|
#include "Engine/DynamicBlueprintBinding.h"
|
|
#include "Blueprint/BlueprintSupport.h"
|
|
|
|
void FEmitDefaultValueHelper::OuterGenerate(FEmitterLocalContext& Context
|
|
, const UProperty* Property
|
|
, const FString& OuterPath
|
|
, const uint8* DataContainer
|
|
, const uint8* OptionalDefaultDataContainer
|
|
, EPropertyAccessOperator AccessOperator
|
|
, bool bAllowProtected)
|
|
{
|
|
// Determine if the given property contains an instanced default subobject reference. We only get here if the values are not identical.
|
|
auto IsInstancedSubobjectLambda = [&](int32 ArrayIndex) -> bool
|
|
{
|
|
if (auto ObjectProperty = Cast<UObjectProperty>(Property))
|
|
{
|
|
check(DataContainer);
|
|
check(OptionalDefaultDataContainer);
|
|
|
|
auto ObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(DataContainer, ArrayIndex);
|
|
auto DefaultObjectPropertyValue = ObjectProperty->GetObjectPropertyValue_InContainer(OptionalDefaultDataContainer, ArrayIndex);
|
|
if (ObjectPropertyValue && ObjectPropertyValue->IsDefaultSubobject() && DefaultObjectPropertyValue && DefaultObjectPropertyValue->IsDefaultSubobject() && ObjectPropertyValue->GetFName() == DefaultObjectPropertyValue->GetFName())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
check(Property);
|
|
if (Property->HasAnyPropertyFlags(CPF_EditorOnly | CPF_Transient))
|
|
{
|
|
UE_LOG(LogK2Compiler, Verbose, TEXT("FEmitDefaultValueHelper Skip EditorOnly or Transient property: %s"), *Property->GetPathName());
|
|
return;
|
|
}
|
|
|
|
if (Property->IsA<UDelegateProperty>() || Property->IsA<UMulticastDelegateProperty>())
|
|
{
|
|
UE_LOG(LogK2Compiler, Verbose, TEXT("FEmitDefaultValueHelper delegate property: %s"), *Property->GetPathName());
|
|
return;
|
|
}
|
|
|
|
for (int32 ArrayIndex = 0; ArrayIndex < Property->ArrayDim; ++ArrayIndex)
|
|
{
|
|
if (!OptionalDefaultDataContainer
|
|
|| ((Property->PropertyFlags & CPF_Config) != 0)
|
|
|| (!Property->Identical_InContainer(DataContainer, OptionalDefaultDataContainer, ArrayIndex) && !IsInstancedSubobjectLambda(ArrayIndex)))
|
|
{
|
|
FNativizationSummaryHelper::PropertyUsed(Context.GetCurrentlyGeneratedClass(), Property);
|
|
|
|
FString PathToMember;
|
|
UBlueprintGeneratedClass* PropertyOwnerAsBPGC = Cast<UBlueprintGeneratedClass>(Property->GetOwnerClass());
|
|
UScriptStruct* PropertyOwnerAsScriptStruct = Cast<UScriptStruct>(Property->GetOwnerStruct());
|
|
const bool bNoExportProperty = PropertyOwnerAsScriptStruct
|
|
&& PropertyOwnerAsScriptStruct->IsNative()
|
|
&& (PropertyOwnerAsScriptStruct->StructFlags & STRUCT_NoExport)
|
|
// && !PropertyOwnerAsScriptStruct->GetBoolMetaData(TEXT("BlueprintType"))
|
|
&& ensure(EPropertyAccessOperator::Dot == AccessOperator);
|
|
if (PropertyOwnerAsBPGC && !Context.Dependencies.WillClassBeConverted(PropertyOwnerAsBPGC))
|
|
{
|
|
ensure(EPropertyAccessOperator::None != AccessOperator);
|
|
const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT("");
|
|
const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : FString::Printf(TEXT("%s(%s)"), *OperatorStr, *OuterPath);
|
|
|
|
PathToMember = FString::Printf(TEXT("FUnconvertedWrapper__%s(%s).GetRef__%s()"), *FEmitHelper::GetCppName(PropertyOwnerAsBPGC), *ContainerStr
|
|
, *UnicodeToCPPIdentifier(Property->GetName(), false, nullptr));
|
|
}
|
|
else if (bNoExportProperty || Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierPrivate) || (!bAllowProtected && Property->HasAnyPropertyFlags(CPF_NativeAccessSpecifierProtected)))
|
|
{
|
|
const UBoolProperty* BoolProperty = Cast<const UBoolProperty>(Property);
|
|
const bool bBietfield = BoolProperty && !BoolProperty->IsNativeBool();
|
|
const FString OperatorStr = (EPropertyAccessOperator::Dot == AccessOperator) ? TEXT("&") : TEXT("");
|
|
const FString ContainerStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("this") : OuterPath;
|
|
if (bBietfield)
|
|
{
|
|
const FString PropertyLocalName = FEmitHelper::GenerateGetPropertyByName(Context, Property);
|
|
const FString ValueStr = Context.ExportTextItem(Property, Property->ContainerPtrToValuePtr<uint8>(DataContainer, ArrayIndex));
|
|
Context.AddLine(FString::Printf(TEXT("(((UBoolProperty*)%s)->%s(%s(%s), %s, %d));")
|
|
, *PropertyLocalName
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(UBoolProperty, SetPropertyValue_InContainer)
|
|
, *OperatorStr
|
|
, *ContainerStr
|
|
, *ValueStr
|
|
, ArrayIndex));
|
|
continue;
|
|
}
|
|
const FString GetPtrStr = bNoExportProperty
|
|
? FEmitHelper::AccessInaccessiblePropertyUsingOffset(Context, Property, ContainerStr, OperatorStr, ArrayIndex)
|
|
: FEmitHelper::AccessInaccessibleProperty(Context, Property, ContainerStr, OperatorStr, ArrayIndex, ENativizedTermUsage::UnspecifiedOrReference, nullptr);
|
|
PathToMember = Context.GenerateUniqueLocalName();
|
|
Context.AddLine(FString::Printf(TEXT("auto& %s = %s;"), *PathToMember, *GetPtrStr));
|
|
}
|
|
else
|
|
{
|
|
const FString AccessOperatorStr = (EPropertyAccessOperator::None == AccessOperator) ? TEXT("")
|
|
: ((EPropertyAccessOperator::Pointer == AccessOperator) ? TEXT("->") : TEXT("."));
|
|
const bool bStaticArray = (Property->ArrayDim > 1);
|
|
const FString ArrayPost = bStaticArray ? FString::Printf(TEXT("[%d]"), ArrayIndex) : TEXT("");
|
|
PathToMember = FString::Printf(TEXT("%s%s%s%s"), *OuterPath, *AccessOperatorStr, *FEmitHelper::GetCppName(Property), *ArrayPost);
|
|
}
|
|
|
|
{
|
|
const uint8* ValuePtr = Property->ContainerPtrToValuePtr<uint8>(DataContainer, ArrayIndex);
|
|
const uint8* DefaultValuePtr = OptionalDefaultDataContainer ? Property->ContainerPtrToValuePtr<uint8>(OptionalDefaultDataContainer, ArrayIndex) : nullptr;
|
|
InnerGenerate(Context, Property, PathToMember, ValuePtr, DefaultValuePtr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FEmitDefaultValueHelper::GenerateGetDefaultValue(const UUserDefinedStruct* Struct, FEmitterLocalContext& Context)
|
|
{
|
|
check(Struct);
|
|
const FString StructName = FEmitHelper::GetCppName(Struct);
|
|
Context.Header.AddLine(FString::Printf(TEXT("static %s GetDefaultValue()"), *StructName));
|
|
Context.Header.AddLine(TEXT("{"));
|
|
|
|
Context.Header.IncreaseIndent();
|
|
Context.Header.AddLine(FString::Printf(TEXT("FStructOnScope StructOnScope(%s::StaticStruct());"), *StructName));
|
|
Context.Header.AddLine(FString::Printf(TEXT("%s& DefaultData__ = *((%s*)StructOnScope.GetStructMemory());"), *StructName, *StructName));
|
|
{
|
|
TGuardValue<FCodeText*> OriginalDefaultTarget(Context.DefaultTarget, &Context.Header);
|
|
FStructOnScope StructData(Struct);
|
|
FStructureEditorUtils::Fill_MakeStructureDefaultValue(Struct, StructData.GetStructMemory());
|
|
FStructOnScope RawDefaultStructOnScope(Struct);
|
|
for (auto Property : TFieldRange<const UProperty>(Struct))
|
|
{
|
|
OuterGenerate(Context, Property, TEXT("DefaultData__"), StructData.GetStructMemory(), RawDefaultStructOnScope.GetStructMemory(), EPropertyAccessOperator::Dot);
|
|
}
|
|
}
|
|
Context.Header.AddLine(TEXT("return DefaultData__;"));
|
|
Context.Header.DecreaseIndent();
|
|
|
|
Context.Header.AddLine(TEXT("}"));
|
|
}
|
|
|
|
void FEmitDefaultValueHelper::InnerGenerate(FEmitterLocalContext& Context, const UProperty* Property, const FString& PathToMember, const uint8* ValuePtr, const uint8* DefaultValuePtr, bool bWithoutFirstConstructionLine)
|
|
{
|
|
auto OneLineConstruction = [](FEmitterLocalContext& LocalContext, const UProperty* LocalProperty, const uint8* LocalValuePtr, FString& OutSingleLine, bool bGenerateEmptyStructConstructor) -> bool
|
|
{
|
|
bool bComplete = true;
|
|
FString ValueStr = HandleSpecialTypes(LocalContext, LocalProperty, LocalValuePtr);
|
|
if (ValueStr.IsEmpty())
|
|
{
|
|
ValueStr = LocalContext.ExportTextItem(LocalProperty, LocalValuePtr);
|
|
auto StructProperty = Cast<const UStructProperty>(LocalProperty);
|
|
if (ValueStr.IsEmpty() && StructProperty)
|
|
{
|
|
check(StructProperty->Struct);
|
|
if (bGenerateEmptyStructConstructor)
|
|
{
|
|
ValueStr = FString::Printf(TEXT("%s%s"), *FEmitHelper::GetCppName(StructProperty->Struct), FEmitHelper::EmptyDefaultConstructor(StructProperty->Struct)); //don;t override existing values
|
|
}
|
|
bComplete = false;
|
|
}
|
|
else if (ValueStr.IsEmpty())
|
|
{
|
|
UE_LOG(LogK2Compiler, Error, TEXT("FEmitDefaultValueHelper Cannot generate initialization: %s"), *LocalProperty->GetPathName());
|
|
}
|
|
}
|
|
OutSingleLine += ValueStr;
|
|
return bComplete;
|
|
};
|
|
|
|
const UStructProperty* StructProperty = Cast<const UStructProperty>(Property);
|
|
check(!StructProperty || StructProperty->Struct);
|
|
const UArrayProperty* ArrayProperty = Cast<const UArrayProperty>(Property);
|
|
check(!ArrayProperty || ArrayProperty->Inner);
|
|
const USetProperty* SetProperty = Cast<const USetProperty>(Property);
|
|
check(!SetProperty || SetProperty->ElementProp);
|
|
const UMapProperty* MapProperty = Cast<const UMapProperty>(Property);
|
|
check(!MapProperty || (MapProperty->KeyProp && MapProperty->ValueProp));
|
|
|
|
if (!bWithoutFirstConstructionLine)
|
|
{
|
|
FString ValueStr;
|
|
const bool bComplete = OneLineConstruction(Context, Property, ValuePtr, ValueStr, false);
|
|
if (!ValueStr.IsEmpty())
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("%s = %s;"), *PathToMember, *ValueStr));
|
|
}
|
|
// array initialization "array_var = TArray<..>()" is complete, but it still needs items.
|
|
if (bComplete && !ArrayProperty && !SetProperty && !MapProperty)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (StructProperty)
|
|
{
|
|
// Create default struct instance, only when DefaultValuePtr is null.
|
|
FStructOnScope DefaultStructOnScope(DefaultValuePtr ? nullptr : StructProperty->Struct);
|
|
|
|
for (auto LocalProperty : TFieldRange<const UProperty>(StructProperty->Struct))
|
|
{
|
|
OuterGenerate(Context, LocalProperty, PathToMember, ValuePtr
|
|
, (DefaultValuePtr ? DefaultValuePtr : DefaultStructOnScope.GetStructMemory())
|
|
, EPropertyAccessOperator::Dot);
|
|
}
|
|
}
|
|
|
|
if (ArrayProperty)
|
|
{
|
|
FScriptArrayHelper ScriptArrayHelper(ArrayProperty, ValuePtr);
|
|
if (ScriptArrayHelper.Num())
|
|
{
|
|
const UStructProperty* InnerStructProperty = Cast<const UStructProperty>(ArrayProperty->Inner);
|
|
const bool bInitializeWithoutScriptStruct = InnerStructProperty && InnerStructProperty->Struct
|
|
&& InnerStructProperty->Struct->IsNative()
|
|
&& (InnerStructProperty->Struct->StructFlags & STRUCT_NoExport);
|
|
const UScriptStruct* RegularInnerStruct = nullptr;
|
|
if(!bInitializeWithoutScriptStruct)
|
|
{
|
|
if (InnerStructProperty && !FEmitDefaultValueHelper::SpecialStructureConstructor(InnerStructProperty->Struct, nullptr, nullptr))
|
|
{
|
|
RegularInnerStruct = InnerStructProperty->Struct;
|
|
}
|
|
}
|
|
|
|
if(RegularInnerStruct)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("%s.%s(%d);"), *PathToMember, TEXT("AddUninitialized"), ScriptArrayHelper.Num()));
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s(%s.GetData(), %d);")
|
|
, *Context.FindGloballyMappedObject(RegularInnerStruct, UScriptStruct::StaticClass())
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(UStruct, InitializeStruct)
|
|
, *PathToMember
|
|
, ScriptArrayHelper.Num()));
|
|
|
|
const FStructOnScope DefaultStruct(RegularInnerStruct);
|
|
for (int32 Index = 0; Index < ScriptArrayHelper.Num(); ++Index)
|
|
{
|
|
const FString ArrayElementRefName = Context.GenerateUniqueLocalName();
|
|
Context.AddLine(FString::Printf(TEXT("auto& %s = %s[%d];"), *ArrayElementRefName, *PathToMember, Index));
|
|
InnerGenerate(Context, ArrayProperty->Inner, ArrayElementRefName, ScriptArrayHelper.GetRawPtr(Index), nullptr, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("%s.%s(%d);"), *PathToMember, TEXT("Reserve"), ScriptArrayHelper.Num()));
|
|
|
|
for (int32 Index = 0; Index < ScriptArrayHelper.Num(); ++Index)
|
|
{
|
|
const uint8* LocalValuePtr = ScriptArrayHelper.GetRawPtr(Index);
|
|
|
|
FString ValueStr;
|
|
bool bComplete = OneLineConstruction(Context, ArrayProperty->Inner, LocalValuePtr, ValueStr, bInitializeWithoutScriptStruct);
|
|
ensure(bComplete || bInitializeWithoutScriptStruct);
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *ValueStr));
|
|
|
|
if (bInitializeWithoutScriptStruct && !bComplete)
|
|
{
|
|
InnerGenerate(Context, ArrayProperty->Inner, FString::Printf(TEXT("%s[%d]"), *PathToMember, Index), LocalValuePtr, nullptr, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(SetProperty)
|
|
{
|
|
FScriptSetHelper ScriptSetHelper(SetProperty, ValuePtr);
|
|
|
|
if (ScriptSetHelper.Num())
|
|
{
|
|
const UStructProperty* ElementStructProperty = Cast<const UStructProperty>(SetProperty->ElementProp);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptSetHelper.Num()));
|
|
|
|
if(ElementStructProperty)
|
|
{
|
|
// declare a temporary scratch struct for the purpose of initialization. We create a scope so that
|
|
// we don't have to generate a new name for it every time we initialize a TSet:
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s SetMemberScratchInitializer;"), *FEmitHelper::GetCppName(ElementStructProperty->Struct)));
|
|
|
|
int32 Size = ScriptSetHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptSetHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
// populate SetMemberScratchInitializer:
|
|
InnerGenerate(Context, SetProperty->ElementProp, TEXT("SetMemberScratchInitializer"), ScriptSetHelper.GetElementPtr(I), nullptr, true);
|
|
|
|
// Move into the TSet:
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(SetMemberScratchInitializer));"), *PathToMember));
|
|
|
|
// reset SetMemberScratchInitializer:
|
|
Context.AddLine(FString::Printf(TEXT("SetMemberScratchInitializer = %s();"), *FEmitHelper::GetCppName(ElementStructProperty->Struct)));
|
|
}
|
|
}
|
|
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
else
|
|
{
|
|
int32 Size = ScriptSetHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptSetHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
FString ValueStr;
|
|
ensure(OneLineConstruction(Context, SetProperty->ElementProp, ScriptSetHelper.GetElementPtr(I), ValueStr, false));
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(%s);"), *PathToMember, *ValueStr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(MapProperty)
|
|
{
|
|
FScriptMapHelper ScriptMapHelper(MapProperty, ValuePtr);
|
|
if (ScriptMapHelper.Num())
|
|
{
|
|
const UStructProperty* KeyStructProperty = Cast<const UStructProperty>(MapProperty->KeyProp);
|
|
const UStructProperty* ValueStructProperty = Cast<const UStructProperty>(MapProperty->ValueProp);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s.Reserve(%d);"), *PathToMember, ScriptMapHelper.Num()));
|
|
|
|
// Split into 4 cases. They are all similar (add the Key/Value to the Map), but structs are significantly more complicated to initialize than primitives:
|
|
if(KeyStructProperty && ValueStructProperty)
|
|
{
|
|
// will add indentation and opening/closing brackets
|
|
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
|
|
// (removes them from the cache so we don't try to use them outside of the scope)
|
|
FScopeBlock NestedScopeBlock(Context);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s MapKeyScratchInitializer;"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
|
|
Context.AddLine(FString::Printf(TEXT("%s MapValueScratchInitializer;"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
|
|
|
|
int32 Size = ScriptMapHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptMapHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
// populate MapKeyScratchInitializer and MapValueScratchInitializer:
|
|
InnerGenerate(Context, MapProperty->KeyProp, TEXT("MapKeyScratchInitializer"), ScriptMapHelper.GetKeyPtr(I), nullptr, true);
|
|
InnerGenerate(Context, MapProperty->ValueProp, TEXT("MapValueScratchInitializer"), ScriptMapHelper.GetValuePtr(I), nullptr, true);
|
|
|
|
// Move into the TMap:
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(MapKeyScratchInitializer), MoveTemp(MapValueScratchInitializer));"), *PathToMember));
|
|
|
|
// reset MapKeyScratchInitializer and MapValueScratchInitializer:
|
|
Context.AddLine(FString::Printf(TEXT("MapKeyScratchInitializer = %s();"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
|
|
Context.AddLine(FString::Printf(TEXT("MapValueScratchInitializer = %s();"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
|
|
}
|
|
}
|
|
}
|
|
else if( KeyStructProperty )
|
|
{
|
|
// will add indentation and opening/closing brackets
|
|
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
|
|
// (removes them from the cache so we don't try to use them outside of the scope)
|
|
FScopeBlock NestedScopeBlock(Context);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s MapKeyScratchInitializer;"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
|
|
|
|
int32 Size = ScriptMapHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptMapHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
// populate MapKeyScratchInitializer:
|
|
InnerGenerate(Context, MapProperty->KeyProp, TEXT("MapKeyScratchInitializer"), ScriptMapHelper.GetKeyPtr(I), nullptr, true);
|
|
|
|
FString ValueStr;
|
|
ensure(OneLineConstruction(Context, MapProperty->ValueProp, ScriptMapHelper.GetValuePtr(I), ValueStr, false));
|
|
|
|
// Move into the TMap:
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(MoveTemp(MapKeyScratchInitializer), %s);"), *PathToMember, *ValueStr));
|
|
|
|
// reset MapKeyScratchInitializer:
|
|
Context.AddLine(FString::Printf(TEXT("MapKeyScratchInitializer = %s();"), *FEmitHelper::GetCppName(KeyStructProperty->Struct)));
|
|
}
|
|
}
|
|
}
|
|
else if( ValueStructProperty )
|
|
{
|
|
// will add indentation and opening/closing brackets
|
|
// used to manage locals that were added via FEmitHelper::GenerateGetPropertyByName()
|
|
// (removes them from the cache so we don't try to use them outside of the scope)
|
|
FScopeBlock NestedScopeBlock(Context);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s MapValueScratchInitializer;"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
|
|
|
|
int32 Size = ScriptMapHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptMapHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
// populate MapValueScratchInitializer:
|
|
InnerGenerate(Context, MapProperty->ValueProp, TEXT("MapValueScratchInitializer"), ScriptMapHelper.GetValuePtr(I), nullptr, true);
|
|
|
|
FString KeyStr;
|
|
ensure(OneLineConstruction(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(I), KeyStr, false));
|
|
|
|
// Move into the TMap:
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(%s, MoveTemp(MapValueScratchInitializer));"), *PathToMember, *KeyStr));
|
|
|
|
// reset MapValueScratchInitializer:
|
|
Context.AddLine(FString::Printf(TEXT("MapValueScratchInitializer = %s();"), *FEmitHelper::GetCppName(ValueStructProperty->Struct)));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int32 Size = ScriptMapHelper.Num();
|
|
for (int32 I = 0; Size; ++I)
|
|
{
|
|
if(ScriptMapHelper.IsValidIndex(I))
|
|
{
|
|
--Size;
|
|
|
|
FString KeyStr;
|
|
ensure(OneLineConstruction(Context, MapProperty->KeyProp, ScriptMapHelper.GetKeyPtr(I), KeyStr, false));
|
|
FString ValueStr;
|
|
ensure(OneLineConstruction(Context, MapProperty->ValueProp, ScriptMapHelper.GetValuePtr(I), ValueStr, false));
|
|
Context.AddLine(FString::Printf(TEXT("%s.Add(%s, %s);"), *PathToMember, *KeyStr, *ValueStr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FEmitDefaultValueHelper::SpecialStructureConstructor(const UStruct* Struct, const uint8* ValuePtr, /*out*/ FString* OutResult)
|
|
{
|
|
check(ValuePtr || !OutResult);
|
|
|
|
if (FLatentActionInfo::StaticStruct() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FLatentActionInfo* LatentActionInfo = reinterpret_cast<const FLatentActionInfo*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FLatentActionInfo(%d, %d, TEXT(\"%s\"), this)")
|
|
, LatentActionInfo->Linkage
|
|
, LatentActionInfo->UUID
|
|
, *LatentActionInfo->ExecutionFunction.ToString().ReplaceCharWithEscapedChar());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FTransform>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FTransform* Transform = reinterpret_cast<const FTransform*>(ValuePtr);
|
|
const auto Rotation = Transform->GetRotation();
|
|
const auto Translation = Transform->GetTranslation();
|
|
const auto Scale = Transform->GetScale3D();
|
|
*OutResult = FString::Printf(TEXT("FTransform( FQuat(%s,%s,%s,%s), FVector(%s,%s,%s), FVector(%s,%s,%s) )"),
|
|
*FEmitHelper::FloatToString(Rotation.X), *FEmitHelper::FloatToString(Rotation.Y), *FEmitHelper::FloatToString(Rotation.Z), *FEmitHelper::FloatToString(Rotation.W),
|
|
*FEmitHelper::FloatToString(Translation.X), *FEmitHelper::FloatToString(Translation.Y), *FEmitHelper::FloatToString(Translation.Z),
|
|
*FEmitHelper::FloatToString(Scale.X), *FEmitHelper::FloatToString(Scale.Y), *FEmitHelper::FloatToString(Scale.Z));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FVector>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FVector* Vector = reinterpret_cast<const FVector*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FVector(%s, %s, %s)"), *FEmitHelper::FloatToString(Vector->X), *FEmitHelper::FloatToString(Vector->Y), *FEmitHelper::FloatToString(Vector->Z));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FGuid>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FGuid* Guid = reinterpret_cast<const FGuid*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FGuid(0x%08X, 0x%08X, 0x%08X, 0x%08X)"), Guid->A, Guid->B, Guid->C, Guid->D);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FRotator>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FRotator* Rotator = reinterpret_cast<const FRotator*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FRotator(%s, %s, %s)"), *FEmitHelper::FloatToString(Rotator->Pitch), *FEmitHelper::FloatToString(Rotator->Yaw), *FEmitHelper::FloatToString(Rotator->Roll));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FLinearColor>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FLinearColor* LinearColor = reinterpret_cast<const FLinearColor*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FLinearColor(%s, %s, %s, %s)"), *FEmitHelper::FloatToString(LinearColor->R), *FEmitHelper::FloatToString(LinearColor->G), *FEmitHelper::FloatToString(LinearColor->B), *FEmitHelper::FloatToString(LinearColor->A));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FColor>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FColor* Color = reinterpret_cast<const FColor*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FColor(%d, %d, %d, %d)"), Color->R, Color->G, Color->B, Color->A);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FVector2D>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FVector2D* Vector2D = reinterpret_cast<const FVector2D*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FVector2D(%s, %s)"), *FEmitHelper::FloatToString(Vector2D->X), *FEmitHelper::FloatToString(Vector2D->Y));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FBox2D>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FBox2D* Box2D = reinterpret_cast<const FBox2D*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("CreateFBox2D(FVector2D(%s, %s), FVector2D(%s, %s), %s)")
|
|
, *FEmitHelper::FloatToString(Box2D->Min.X)
|
|
, *FEmitHelper::FloatToString(Box2D->Min.Y)
|
|
, *FEmitHelper::FloatToString(Box2D->Max.X)
|
|
, *FEmitHelper::FloatToString(Box2D->Max.Y)
|
|
, Box2D->bIsValid ? TEXT("true") : TEXT("false"));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FFloatRangeBound>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FFloatRangeBound* FloatRangeBound = reinterpret_cast<const FFloatRangeBound*>(ValuePtr);
|
|
if (FloatRangeBound->IsExclusive())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s(%s)"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Exclusive), *FEmitHelper::FloatToString(FloatRangeBound->GetValue()));
|
|
}
|
|
if (FloatRangeBound->IsInclusive())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s(%s)"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Inclusive), *FEmitHelper::FloatToString(FloatRangeBound->GetValue()));
|
|
}
|
|
if (FloatRangeBound->IsOpen())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FFloatRangeBound::%s()"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Open));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FFloatRange>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FFloatRange* FloatRangeBound = reinterpret_cast<const FFloatRange*>(ValuePtr);
|
|
|
|
FString LowerBoundStr;
|
|
FFloatRangeBound LowerBound = FloatRangeBound->GetLowerBound();
|
|
SpecialStructureConstructor(TBaseStructure<FFloatRangeBound>::Get(), (uint8*)&LowerBound, &LowerBoundStr);
|
|
|
|
FString UpperBoundStr;
|
|
FFloatRangeBound UpperBound = FloatRangeBound->GetUpperBound();
|
|
SpecialStructureConstructor(TBaseStructure<FFloatRangeBound>::Get(), (uint8*)&UpperBound, &UpperBoundStr);
|
|
|
|
*OutResult = FString::Printf(TEXT("FFloatRange(%s, %s)"), *LowerBoundStr, *UpperBoundStr);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FInt32RangeBound>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FInt32RangeBound* RangeBound = reinterpret_cast<const FInt32RangeBound*>(ValuePtr);
|
|
if (RangeBound->IsExclusive())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s(%d)"), GET_FUNCTION_NAME_STRING_CHECKED(FInt32RangeBound, Exclusive), RangeBound->GetValue());
|
|
}
|
|
if (RangeBound->IsInclusive())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s(%d)"), GET_FUNCTION_NAME_STRING_CHECKED(FInt32RangeBound, Exclusive), RangeBound->GetValue());
|
|
}
|
|
if (RangeBound->IsOpen())
|
|
{
|
|
*OutResult = FString::Printf(TEXT("FInt32RangeBound::%s()"), GET_FUNCTION_NAME_STRING_CHECKED(FFloatRangeBound, Open));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FInt32Range>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FInt32Range* RangeBound = reinterpret_cast<const FInt32Range*>(ValuePtr);
|
|
|
|
FString LowerBoundStr;
|
|
FInt32RangeBound LowerBound = RangeBound->GetLowerBound();
|
|
SpecialStructureConstructor(TBaseStructure<FInt32RangeBound>::Get(), (uint8*)&LowerBound, &LowerBoundStr);
|
|
|
|
FString UpperBoundStr;
|
|
FInt32RangeBound UpperBound = RangeBound->GetUpperBound();
|
|
SpecialStructureConstructor(TBaseStructure<FInt32RangeBound>::Get(), (uint8*)&UpperBound, &UpperBoundStr);
|
|
|
|
*OutResult = FString::Printf(TEXT("FInt32Range(%s, %s)"), *LowerBoundStr, *UpperBoundStr);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FFloatInterval>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FFloatInterval* Interval = reinterpret_cast<const FFloatInterval*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FFloatInterval(%s, %s)"), *FEmitHelper::FloatToString(Interval->Min), *FEmitHelper::FloatToString(Interval->Max));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (TBaseStructure<FInt32Interval>::Get() == Struct)
|
|
{
|
|
if (OutResult)
|
|
{
|
|
const FInt32Interval* Interval = reinterpret_cast<const FInt32Interval*>(ValuePtr);
|
|
*OutResult = FString::Printf(TEXT("FFloatInterval(%d, %d)"), Interval->Min, Interval->Max);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FString FEmitDefaultValueHelper::HandleSpecialTypes(FEmitterLocalContext& Context, const UProperty* Property, const uint8* ValuePtr)
|
|
{
|
|
//TODO: Use Path maps for Objects
|
|
if (auto ObjectProperty = Cast<UObjectProperty>(Property))
|
|
{
|
|
UObject* Object = ObjectProperty->GetPropertyValue(ValuePtr);
|
|
if (Object)
|
|
{
|
|
{
|
|
UClass* ObjectClassToUse = Context.GetFirstNativeOrConvertedClass(ObjectProperty->PropertyClass);
|
|
const FString MappedObject = Context.FindGloballyMappedObject(Object, ObjectClassToUse);
|
|
if (!MappedObject.IsEmpty())
|
|
{
|
|
return MappedObject;
|
|
}
|
|
}
|
|
|
|
const bool bCreatingSubObjectsOfClass = (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass);
|
|
{
|
|
auto BPGC = Context.GetCurrentlyGeneratedClass();
|
|
auto CDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr;
|
|
if (BPGC && Object && CDO && Object->IsIn(BPGC) && !Object->IsIn(CDO) && bCreatingSubObjectsOfClass)
|
|
{
|
|
return HandleClassSubobject(Context, Object, FEmitterLocalContext::EClassSubobjectList::MiscConvertedSubobjects, true, true);
|
|
}
|
|
}
|
|
|
|
if (!bCreatingSubObjectsOfClass && Property->HasAnyPropertyFlags(CPF_InstancedReference))
|
|
{
|
|
const FString CreateAsInstancedSubobject = HandleInstancedSubobject(Context, Object, Object->HasAnyFlags(RF_ArchetypeObject));
|
|
if (!CreateAsInstancedSubobject.IsEmpty())
|
|
{
|
|
return CreateAsInstancedSubobject;
|
|
}
|
|
}
|
|
}
|
|
else if (ObjectProperty->HasMetaData(FBlueprintMetadata::MD_LatentCallbackTarget))
|
|
{
|
|
return TEXT("this");
|
|
}
|
|
}
|
|
|
|
if (auto StructProperty = Cast<UStructProperty>(Property))
|
|
{
|
|
FString StructConstructor;
|
|
if (SpecialStructureConstructor(StructProperty->Struct, ValuePtr, &StructConstructor))
|
|
{
|
|
return StructConstructor;
|
|
}
|
|
}
|
|
|
|
return FString();
|
|
}
|
|
|
|
struct FNonativeComponentData
|
|
{
|
|
FString NativeVariablePropertyName;
|
|
UActorComponent* ComponentTemplate;
|
|
UObject* ObjectToCompare;
|
|
|
|
////
|
|
FString ParentVariableName;
|
|
bool bSetNativeCreationMethod;
|
|
/** Socket/Bone that Component might attach to */
|
|
FName AttachToName;
|
|
bool bIsRoot;
|
|
|
|
FNonativeComponentData()
|
|
: ComponentTemplate(nullptr)
|
|
, ObjectToCompare(nullptr)
|
|
, bSetNativeCreationMethod(false)
|
|
, bIsRoot(false)
|
|
{
|
|
}
|
|
|
|
bool HandledAsSpecialProperty(FEmitterLocalContext& Context, const UProperty* Property)
|
|
{
|
|
// skip relative location and rotation. THey are ignored for root components created from scs (and they probably should be reset by scs editor).
|
|
if (bIsRoot && (Property->GetOuter() == USceneComponent::StaticClass()))
|
|
{
|
|
UProperty* RelativeLocationProperty = USceneComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(USceneComponent, RelativeLocation));
|
|
UProperty* RelativeRotationProperty = USceneComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(USceneComponent, RelativeRotation));
|
|
if ((Property == RelativeLocationProperty) || (Property == RelativeRotationProperty))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void EmitProperties(FEmitterLocalContext& Context)
|
|
{
|
|
ensure(!NativeVariablePropertyName.IsEmpty());
|
|
if (bSetNativeCreationMethod)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s = EComponentCreationMethod::Native;"), *NativeVariablePropertyName, GET_MEMBER_NAME_STRING_CHECKED(UActorComponent, CreationMethod)));
|
|
}
|
|
|
|
if (!ParentVariableName.IsEmpty())
|
|
{
|
|
const FString SocketName = (AttachToName == NAME_None) ? FString() : FString::Printf(TEXT(", TEXT(\"%s\")"), *AttachToName.ToString());
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s(%s, FAttachmentTransformRules::KeepRelativeTransform %s);")
|
|
, *NativeVariablePropertyName
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(USceneComponent, AttachToComponent)
|
|
, *ParentVariableName, *SocketName));
|
|
// AttachTo is called first in case some properties will be overridden.
|
|
}
|
|
|
|
bool bBodyInstanceIsAlreadyHandled = false;
|
|
UProperty* BodyInstanceProperty = UPrimitiveComponent::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UPrimitiveComponent, BodyInstance));
|
|
UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(ComponentTemplate);
|
|
if(PrimitiveComponent)
|
|
{
|
|
const FName CollisionProfileName = PrimitiveComponent->BodyInstance.GetCollisionProfileName();
|
|
UPrimitiveComponent* ComponentArchetype = Cast<UPrimitiveComponent>(ObjectToCompare);
|
|
const FName ComponentArchetypeCollisionProfileName = ComponentArchetype ? ComponentArchetype->BodyInstance.GetCollisionProfileName() : NAME_None;
|
|
if (CollisionProfileName != ComponentArchetypeCollisionProfileName)
|
|
{
|
|
FStructOnScope BodyInstanceToCompare(FBodyInstance::StaticStruct());
|
|
if (ComponentArchetype)
|
|
{
|
|
FBodyInstance::StaticStruct()->CopyScriptStruct(BodyInstanceToCompare.GetStructMemory(), &ComponentArchetype->BodyInstance);
|
|
}
|
|
((FBodyInstance*)BodyInstanceToCompare.GetStructMemory())->SetCollisionProfileName(CollisionProfileName);
|
|
|
|
const FString PathToMember = FString::Printf(TEXT("%s->BodyInstance"), *NativeVariablePropertyName);
|
|
Context.AddLine(FString::Printf(TEXT("%s.SetCollisionProfileName(FName(TEXT(\"%s\")));"), *PathToMember, *CollisionProfileName.ToString().ReplaceCharWithEscapedChar()));
|
|
FEmitDefaultValueHelper::InnerGenerate(Context, BodyInstanceProperty, PathToMember, (const uint8*)&PrimitiveComponent->BodyInstance, BodyInstanceToCompare.GetStructMemory());
|
|
bBodyInstanceIsAlreadyHandled = true;
|
|
}
|
|
}
|
|
|
|
UClass* ComponentClass = ComponentTemplate->GetClass();
|
|
for (auto Property : TFieldRange<const UProperty>(ComponentClass))
|
|
{
|
|
if (bBodyInstanceIsAlreadyHandled && (Property == BodyInstanceProperty))
|
|
{
|
|
continue;
|
|
}
|
|
if (HandledAsSpecialProperty(Context, Property))
|
|
{
|
|
continue;
|
|
}
|
|
FEmitDefaultValueHelper::OuterGenerate(Context, Property, NativeVariablePropertyName
|
|
, reinterpret_cast<const uint8*>(ComponentTemplate)
|
|
, reinterpret_cast<const uint8*>(ObjectToCompare)
|
|
, FEmitDefaultValueHelper::EPropertyAccessOperator::Pointer);
|
|
}
|
|
}
|
|
|
|
void EmitForcedPostLoad(FEmitterLocalContext& Context)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("if(%s && !%s->%s())"), *NativeVariablePropertyName, *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, IsTemplate)));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s(RF_NeedPostLoad |RF_NeedPostLoadSubobjects);"), *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, SetFlags)));
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s();"), *NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UActorComponent, ConditionalPostLoad)));
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
};
|
|
|
|
FString FEmitDefaultValueHelper::HandleNonNativeComponent(FEmitterLocalContext& Context, const USCS_Node* Node, TSet<const UProperty*>& OutHandledProperties, TArray<FString>& NativeCreatedComponentProperties, const USCS_Node* ParentNode, TArray<FNonativeComponentData>& ComponenntsToInit)
|
|
{
|
|
check(Node);
|
|
check(Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::CommonConstructor);
|
|
|
|
FString NativeVariablePropertyName;
|
|
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
|
|
if (UActorComponent* ComponentTemplate = Node->GetActualComponentTemplate(BPGC))
|
|
{
|
|
const FString VariableCleanName = Node->GetVariableName().ToString();
|
|
|
|
const UObjectProperty* VariableProperty = FindField<UObjectProperty>(BPGC, *VariableCleanName);
|
|
if (VariableProperty)
|
|
{
|
|
NativeVariablePropertyName = FEmitHelper::GetCppName(VariableProperty);
|
|
OutHandledProperties.Add(VariableProperty);
|
|
}
|
|
else
|
|
{
|
|
NativeVariablePropertyName = VariableCleanName;
|
|
}
|
|
|
|
Context.AddCommonSubObject_InConstructor(ComponentTemplate, NativeVariablePropertyName);
|
|
|
|
if (ComponentTemplate->GetOuter() == BPGC)
|
|
{
|
|
FNonativeComponentData NonativeComponentData;
|
|
NonativeComponentData.NativeVariablePropertyName = NativeVariablePropertyName;
|
|
NonativeComponentData.ComponentTemplate = ComponentTemplate;
|
|
USCS_Node* RootComponentNode = nullptr;
|
|
Node->GetSCS()->GetSceneRootComponentTemplate(&RootComponentNode);
|
|
NonativeComponentData.bIsRoot = RootComponentNode == Node;
|
|
UClass* ComponentClass = ComponentTemplate->GetClass();
|
|
check(ComponentClass != nullptr);
|
|
|
|
UObject* ObjectToCompare = ComponentClass->GetDefaultObject(false);
|
|
|
|
if (ComponentTemplate->HasAnyFlags(RF_InheritableComponentTemplate))
|
|
{
|
|
ObjectToCompare = Node->GetActualComponentTemplate(Cast<UBlueprintGeneratedClass>(BPGC->GetSuperClass()));
|
|
}
|
|
else
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("%s%s = CreateDefaultSubobject<%s>(TEXT(\"%s\"));")
|
|
, (VariableProperty == nullptr) ? TEXT("auto ") : TEXT("")
|
|
, *NativeVariablePropertyName
|
|
, *FEmitHelper::GetCppName(ComponentClass)
|
|
, *VariableCleanName));
|
|
|
|
NonativeComponentData.bSetNativeCreationMethod = true;
|
|
NativeCreatedComponentProperties.Add(NativeVariablePropertyName);
|
|
|
|
FString ParentVariableName;
|
|
if (ParentNode)
|
|
{
|
|
const FString CleanParentVariableName = ParentNode->GetVariableName().ToString();
|
|
const UObjectProperty* ParentVariableProperty = FindField<UObjectProperty>(BPGC, *CleanParentVariableName);
|
|
ParentVariableName = ParentVariableProperty ? FEmitHelper::GetCppName(ParentVariableProperty) : CleanParentVariableName;
|
|
}
|
|
else if (USceneComponent* ParentComponentTemplate = Node->GetParentComponentTemplate(CastChecked<UBlueprint>(BPGC->ClassGeneratedBy)))
|
|
{
|
|
ParentVariableName = Context.FindGloballyMappedObject(ParentComponentTemplate, USceneComponent::StaticClass());
|
|
}
|
|
NonativeComponentData.ParentVariableName = ParentVariableName;
|
|
NonativeComponentData.AttachToName = Node->AttachToName;
|
|
}
|
|
NonativeComponentData.ObjectToCompare = ObjectToCompare;
|
|
ComponenntsToInit.Add(NonativeComponentData);
|
|
}
|
|
}
|
|
|
|
// Recursively handle child nodes.
|
|
for (auto ChildNode : Node->ChildNodes)
|
|
{
|
|
HandleNonNativeComponent(Context, ChildNode, OutHandledProperties, NativeCreatedComponentProperties, Node, ComponenntsToInit);
|
|
}
|
|
|
|
return NativeVariablePropertyName;
|
|
}
|
|
|
|
struct FDependenciesHelper
|
|
{
|
|
public:
|
|
// Keep sync with FTypeSingletonCache::GenerateSingletonName
|
|
static FString GenerateZConstructor(UField* Item)
|
|
{
|
|
FString Result;
|
|
if (!ensure(Item))
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
for (UObject* Outer = Item; Outer; Outer = Outer->GetOuter())
|
|
{
|
|
if (!Result.IsEmpty())
|
|
{
|
|
Result = TEXT("_") + Result;
|
|
}
|
|
|
|
if (Cast<UClass>(Outer) || Cast<UScriptStruct>(Outer))
|
|
{
|
|
FString OuterName = FEmitHelper::GetCppName(CastChecked<UField>(Outer), true);
|
|
Result = OuterName + Result;
|
|
|
|
// Structs can also have UPackage outer.
|
|
if (Cast<UClass>(Outer) || Cast<UPackage>(Outer->GetOuter()))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = Outer->GetName() + Result;
|
|
}
|
|
}
|
|
|
|
// Can't use long package names in function names.
|
|
if (Result.StartsWith(TEXT("/Script/"), ESearchCase::CaseSensitive))
|
|
{
|
|
Result = FPackageName::GetShortName(Result);
|
|
}
|
|
|
|
const FString ClassString = Item->IsA<UClass>() ? TEXT("UClass") : TEXT("UScriptStruct");
|
|
return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + Result + TEXT("()");
|
|
}
|
|
};
|
|
|
|
struct FFakeImportTableHelper
|
|
{
|
|
TSet<UObject*> SerializeBeforeSerializeClassDependencies;
|
|
|
|
TSet<UObject*> SerializeBeforeCreateCDODependencies;
|
|
|
|
FFakeImportTableHelper(UClass* SourceClass, UClass* OriginalClass, FEmitterLocalContext& Context)
|
|
{
|
|
if (ensure(SourceClass) && ensure(OriginalClass))
|
|
{
|
|
auto GatherDependencies = [&](UClass* InClass)
|
|
{
|
|
SerializeBeforeSerializeClassDependencies.Add(InClass->GetSuperClass());
|
|
|
|
for (const FImplementedInterface& ImplementedInterface : InClass->Interfaces)
|
|
{
|
|
SerializeBeforeSerializeClassDependencies.Add(ImplementedInterface.Class);
|
|
}
|
|
|
|
TArray<UObject*> ObjectsInsideClass;
|
|
GetObjectsWithOuter(InClass, ObjectsInsideClass, true);
|
|
for (UObject* Obj : ObjectsInsideClass)
|
|
{
|
|
UProperty* Property = Cast<UProperty>(Obj);
|
|
if (!Property)
|
|
{
|
|
continue;
|
|
}
|
|
const UProperty* OwnerProperty = Property->GetOwnerProperty();
|
|
if (!ensure(IsValid(OwnerProperty)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// TODO:
|
|
// Let UDS_A contain UDS_B. Let UDS_B contain an array or a set of UDS_A. It causes a cyclic dependency.
|
|
// Should we try to fix it at this stage?
|
|
|
|
const bool bIsParam = (0 != (OwnerProperty->PropertyFlags & CPF_Parm)) && OwnerProperty->IsIn(InClass);
|
|
const bool bIsMemberVariable = (OwnerProperty->GetOuter() == InClass);
|
|
if (bIsParam || bIsMemberVariable) // Affects the class signature. It is necessary while ZCOnstructor/linking.
|
|
{
|
|
TArray<UObject*> LocalPreloadDependencies;
|
|
Property->GetPreloadDependencies(LocalPreloadDependencies);
|
|
for (UObject* Dependency : LocalPreloadDependencies)
|
|
{
|
|
const bool bDependencyMustBeSerializedBeforeClassIsLinked = Dependency
|
|
&& (Dependency->IsA<UScriptStruct>() || Dependency->IsA<UEnum>());
|
|
if (bDependencyMustBeSerializedBeforeClassIsLinked)
|
|
{
|
|
SerializeBeforeSerializeClassDependencies.Add(Dependency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SerializeBeforeCreateCDODependencies.Add(InClass->GetSuperClass()->GetDefaultObject());
|
|
};
|
|
|
|
GatherDependencies(SourceClass);
|
|
GatherDependencies(OriginalClass);
|
|
|
|
auto GetClassesOfSubobjects = [&](TMap<UObject*, FString>& SubobjectsMap)
|
|
{
|
|
TArray<UObject*> Subobjects;
|
|
SubobjectsMap.GetKeys(Subobjects);
|
|
for (UObject* Subobject : Subobjects)
|
|
{
|
|
if (Subobject)
|
|
{
|
|
SerializeBeforeCreateCDODependencies.Add(Subobject->GetClass());
|
|
}
|
|
}
|
|
};
|
|
GetClassesOfSubobjects(Context.ClassSubobjectsMap);
|
|
GetClassesOfSubobjects(Context.CommonSubobjectsMap);
|
|
}
|
|
}
|
|
|
|
void FillDependencyData(const UObject* Asset, FCompactBlueprintDependencyData& CompactDataRef) const
|
|
{
|
|
ensure(Asset);
|
|
|
|
{
|
|
//Dynamic Class requires no non-native class, owner, archetype..
|
|
CompactDataRef.ClassDependency.bSerializationBeforeCreateDependency = false;
|
|
CompactDataRef.ClassDependency.bCreateBeforeCreateDependency = false;
|
|
|
|
const bool bDependencyNecessaryForLinking = SerializeBeforeSerializeClassDependencies.Contains(const_cast<UObject*>(Asset));
|
|
|
|
// Super Class, Interfaces, ScriptStructs, Enums..
|
|
CompactDataRef.ClassDependency.bSerializationBeforeSerializationDependency = bDependencyNecessaryForLinking;
|
|
|
|
// Everything else
|
|
CompactDataRef.ClassDependency.bCreateBeforeSerializationDependency = !bDependencyNecessaryForLinking;
|
|
}
|
|
|
|
{
|
|
//everything was created for class
|
|
CompactDataRef.CDODependency.bCreateBeforeCreateDependency = false;
|
|
|
|
// Classes of subobjects, created while CDO construction
|
|
CompactDataRef.CDODependency.bSerializationBeforeCreateDependency = SerializeBeforeCreateCDODependencies.Contains(const_cast<UObject*>(Asset));
|
|
|
|
// CDO is not serialized
|
|
CompactDataRef.CDODependency.bCreateBeforeSerializationDependency = false;
|
|
CompactDataRef.CDODependency.bSerializationBeforeSerializationDependency = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
void FEmitDefaultValueHelper::AddStaticFunctionsForDependencies(FEmitterLocalContext& Context
|
|
, TSharedPtr<FGatherConvertedClassDependencies> ParentDependencies
|
|
, FCompilerNativizationOptions NativizationOptions)
|
|
{
|
|
// 1. GATHER UDS DEFAULT VALUE DEPENDENCIES
|
|
{
|
|
TSet<UObject*> References;
|
|
for (UUserDefinedStruct* UDS : Context.StructsWithDefaultValuesUsed)
|
|
{
|
|
FGatherConvertedClassDependencies::GatherAssetReferencedByUDSDefaultValue(References, UDS);
|
|
}
|
|
for (UObject* Obj : References)
|
|
{
|
|
Context.UsedObjectInCurrentClass.AddUnique(Obj);
|
|
}
|
|
}
|
|
|
|
// 2. ALL ASSETS TO LIST
|
|
TSet<const UObject*> AllDependenciesToHandle = Context.Dependencies.AllDependencies();
|
|
AllDependenciesToHandle.Append(Context.UsedObjectInCurrentClass);
|
|
AllDependenciesToHandle.Remove(nullptr);
|
|
|
|
// Special case, we don't need to load any dependencies from CoreUObject.
|
|
UPackage* CoreUObjectPackage = UProperty::StaticClass()->GetOutermost();
|
|
for (auto Iter = AllDependenciesToHandle.CreateIterator(); Iter; ++Iter)
|
|
{
|
|
if ((*Iter)->GetOutermost() == CoreUObjectPackage)
|
|
{
|
|
Iter.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
// HELPERS
|
|
auto SourceClass = Context.GetCurrentlyGeneratedClass();
|
|
auto OriginalClass = Context.Dependencies.FindOriginalClass(SourceClass);
|
|
const FString CppClassName = FEmitHelper::GetCppName(SourceClass);
|
|
FFakeImportTableHelper FakeImportTableHelper(SourceClass, OriginalClass, Context);
|
|
|
|
auto CreateAssetToLoadString = [&](const UObject* AssetObj) -> FString
|
|
{
|
|
UClass* AssetType = AssetObj->GetClass();
|
|
if (AssetType->IsChildOf<UUserDefinedEnum>())
|
|
{
|
|
AssetType = UEnum::StaticClass();
|
|
}
|
|
else if (AssetType->IsChildOf<UUserDefinedStruct>())
|
|
{
|
|
AssetType = UScriptStruct::StaticClass();
|
|
}
|
|
else if (AssetType->IsChildOf<UBlueprintGeneratedClass>() && Context.Dependencies.WillClassBeConverted(CastChecked<UBlueprintGeneratedClass>(AssetObj)))
|
|
{
|
|
AssetType = UDynamicClass::StaticClass();
|
|
}
|
|
|
|
const FString LongPackagePath = FPackageName::GetLongPackagePath(AssetObj->GetOutermost()->GetPathName());
|
|
return FString::Printf(TEXT("FBlueprintDependencyObjectRef(TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\"), TEXT(\"%s\")),")
|
|
, *LongPackagePath
|
|
, *FPackageName::GetShortName(AssetObj->GetOutermost()->GetPathName())
|
|
, *AssetObj->GetName()
|
|
, *AssetType->GetOutermost()->GetPathName()
|
|
, *AssetType->GetName());
|
|
};
|
|
|
|
auto CreateDependencyRecord = [&](const UObject* InAsset, FString& OptionalComment) -> FCompactBlueprintDependencyData
|
|
{
|
|
ensure(InAsset);
|
|
if (InAsset && IsEditorOnlyObject(InAsset))
|
|
{
|
|
UE_LOG(LogK2Compiler, Warning, TEXT("Nativized %d depends on editor only asset: %s")
|
|
, (OriginalClass ? *OriginalClass->GetPathName() : *CppClassName)
|
|
,*InAsset->GetPathName());
|
|
OptionalComment = TEXT("Editor Only asset");
|
|
return FCompactBlueprintDependencyData{};
|
|
}
|
|
|
|
{
|
|
bool bNotForClient = false;
|
|
bool bNotForServer = false;
|
|
for (const UObject* Search = InAsset; Search && !Search->IsA(UPackage::StaticClass()); Search = Search->GetOuter())
|
|
{
|
|
bNotForClient = bNotForClient || !Search->NeedsLoadForClient();
|
|
bNotForServer = bNotForServer || !Search->NeedsLoadForServer();
|
|
}
|
|
if (bNotForServer && NativizationOptions.ServerOnlyPlatform)
|
|
{
|
|
OptionalComment = TEXT("Not for server");
|
|
return FCompactBlueprintDependencyData{};
|
|
}
|
|
if (bNotForClient && NativizationOptions.ClientOnlyPlatform)
|
|
{
|
|
OptionalComment = TEXT("Not for client");
|
|
return FCompactBlueprintDependencyData{};
|
|
}
|
|
}
|
|
|
|
FNativizationSummary::FDependencyRecord& DependencyRecord = FDependenciesGlobalMapHelper::FindDependencyRecord(InAsset);
|
|
ensure(DependencyRecord.Index >= 0);
|
|
if (DependencyRecord.NativeLine.IsEmpty())
|
|
{
|
|
DependencyRecord.NativeLine = CreateAssetToLoadString(InAsset);
|
|
}
|
|
|
|
FCompactBlueprintDependencyData Result;
|
|
Result.ObjectRefIndex = static_cast<int16>(DependencyRecord.Index);
|
|
FakeImportTableHelper.FillDependencyData(InAsset, Result);
|
|
return Result;
|
|
};
|
|
|
|
auto AddAssetArray = [&](const TArray<const UObject*>& Assets)
|
|
{
|
|
if (Assets.Num())
|
|
{
|
|
Context.AddLine(TEXT("const FCompactBlueprintDependencyData LocCompactBlueprintDependencyData[] ="));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
}
|
|
|
|
auto BlueprintDependencyTypeToString = [](FBlueprintDependencyType DependencyType) -> FString
|
|
{
|
|
return FString::Printf(TEXT("FBlueprintDependencyType(%s, %s, %s, %s)")
|
|
, DependencyType.bSerializationBeforeSerializationDependency ? TEXT("true") : TEXT("false")
|
|
, DependencyType.bCreateBeforeSerializationDependency ? TEXT("true") : TEXT("false")
|
|
, DependencyType.bSerializationBeforeCreateDependency ? TEXT("true") : TEXT("false")
|
|
, DependencyType.bCreateBeforeCreateDependency ? TEXT("true") : TEXT("false"));
|
|
};
|
|
|
|
for (const UObject* LocAsset : Assets)
|
|
{
|
|
FString OptionalComment;
|
|
const FCompactBlueprintDependencyData DependencyRecord = CreateDependencyRecord(LocAsset, OptionalComment);
|
|
Context.AddLine(FString::Printf(TEXT("{%d, %s, %s}, // %s %s ")
|
|
, DependencyRecord.ObjectRefIndex
|
|
, *BlueprintDependencyTypeToString(DependencyRecord.ClassDependency)
|
|
, *BlueprintDependencyTypeToString(DependencyRecord.CDODependency)
|
|
, *OptionalComment
|
|
, *LocAsset->GetFullName()));
|
|
}
|
|
|
|
if (Assets.Num())
|
|
{
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("};"));
|
|
Context.AddLine(TEXT("for(const FCompactBlueprintDependencyData CompactData : LocCompactBlueprintDependencyData)"));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.AddLine(TEXT("\tAssetsToLoad.Add(FBlueprintDependencyData("));
|
|
Context.AddLine(TEXT("\t\tF__NativeDependencies::Get(CompactData.ObjectRefIndex)"));
|
|
Context.AddLine(TEXT("\t\t,CompactData.ClassDependency"));
|
|
Context.AddLine(TEXT("\t\t,CompactData.CDODependency"));
|
|
Context.AddLine(TEXT("\t));"));
|
|
Context.AddLine(TEXT("} "));
|
|
}
|
|
};
|
|
|
|
// 3. LIST OF UsedAssets
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("void %s::__StaticDependencies_DirectlyUsedAssets(TArray<FBlueprintDependencyData>& AssetsToLoad)"), *CppClassName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
TArray<const UObject*> AssetsToAdd;
|
|
for (int32 UsedAssetIndex = 0; UsedAssetIndex < Context.UsedObjectInCurrentClass.Num(); ++UsedAssetIndex)
|
|
{
|
|
const UObject* LocAsset = Context.UsedObjectInCurrentClass[UsedAssetIndex];
|
|
ensure(AllDependenciesToHandle.Contains(LocAsset));
|
|
AssetsToAdd.Add(LocAsset);
|
|
AllDependenciesToHandle.Remove(LocAsset);
|
|
}
|
|
AddAssetArray(AssetsToAdd);
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
|
|
// 4. REMAINING DEPENDENCIES
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("void %s::__StaticDependenciesAssets(TArray<FBlueprintDependencyData>& AssetsToLoad)"), *CppClassName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString(TEXT("__StaticDependencies_DirectlyUsedAssets(AssetsToLoad);")));
|
|
AddAssetArray(AllDependenciesToHandle.Array());
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
}
|
|
|
|
void FEmitDefaultValueHelper::AddRegisterHelper(FEmitterLocalContext& Context)
|
|
{
|
|
auto SourceClass = Context.GetCurrentlyGeneratedClass();
|
|
auto OriginalClass = Context.Dependencies.FindOriginalClass(SourceClass);
|
|
const FString CppClassName = FEmitHelper::GetCppName(SourceClass);
|
|
|
|
const FString RegisterHelperName = FString::Printf(TEXT("FRegisterHelper__%s"), *CppClassName);
|
|
Context.AddLine(FString::Printf(TEXT("struct %s"), *RegisterHelperName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s()"), *RegisterHelperName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
|
|
Context.AddLine(FString::Printf(
|
|
TEXT("FConvertedBlueprintsDependencies::Get().RegisterConvertedClass(TEXT(\"%s\"), &%s::__StaticDependenciesAssets);")
|
|
, *OriginalClass->GetOutermost()->GetPathName()
|
|
, *CppClassName));
|
|
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
|
|
Context.AddLine(FString::Printf(TEXT("static %s Instance;"), *RegisterHelperName));
|
|
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("};"));
|
|
|
|
Context.AddLine(FString::Printf(TEXT("%s %s::Instance;"), *RegisterHelperName, *RegisterHelperName));
|
|
}
|
|
|
|
void FEmitDefaultValueHelper::GenerateCustomDynamicClassInitialization(FEmitterLocalContext& Context, TSharedPtr<FGatherConvertedClassDependencies> ParentDependencies)
|
|
{
|
|
auto BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
|
|
const FString CppClassName = FEmitHelper::GetCppName(BPGC);
|
|
|
|
Context.AddLine(FString::Printf(TEXT("void %s::__CustomDynamicClassInitialization(UDynamicClass* InDynamicClass)"), *CppClassName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields)));
|
|
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, MiscConvertedSubobjects)));
|
|
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, DynamicBindingObjects)));
|
|
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ComponentTemplates)));
|
|
Context.AddLine(FString::Printf(TEXT("ensure(0 == InDynamicClass->%s.Num());"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, Timelines)));
|
|
Context.AddLine(FString::Printf(TEXT("ensure(nullptr == InDynamicClass->%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, AnimClassImplementation)));
|
|
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s();"), GET_FUNCTION_NAME_STRING_CHECKED(UDynamicClass, AssembleReferenceTokenStream)));
|
|
|
|
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass;
|
|
Context.ResetPropertiesForInaccessibleStructs();
|
|
|
|
if (Context.Dependencies.ConvertedEnum.Num())
|
|
{
|
|
Context.AddLine(TEXT("// List of all referenced converted enums"));
|
|
}
|
|
for (auto LocEnum : Context.Dependencies.ConvertedEnum)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(LoadObject<UEnum>(nullptr, TEXT(\"%s\")));"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *(LocEnum->GetPathName().ReplaceCharWithEscapedChar())));
|
|
Context.EnumsInCurrentClass.Add(LocEnum);
|
|
}
|
|
|
|
if (Context.Dependencies.ConvertedClasses.Num())
|
|
{
|
|
Context.AddLine(TEXT("// List of all referenced converted classes"));
|
|
}
|
|
for (auto LocStruct : Context.Dependencies.ConvertedClasses)
|
|
{
|
|
UClass* ClassToLoad = Context.Dependencies.FindOriginalClass(LocStruct);
|
|
if (ensure(ClassToLoad))
|
|
{
|
|
if (ParentDependencies.IsValid() && ParentDependencies->ConvertedClasses.Contains(LocStruct))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const FString ClassConstructor = FDependenciesHelper::GenerateZConstructor(ClassToLoad);
|
|
Context.AddLine(FString::Printf(TEXT("extern UClass* %s;"), *ClassConstructor));
|
|
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *ClassConstructor));
|
|
|
|
//Context.AddLine(FString::Printf(TEXT("InDynamicClass->ReferencedConvertedFields.Add(LoadObject<UClass>(nullptr, TEXT(\"%s\")));")
|
|
// , *(ClassToLoad->GetPathName().ReplaceCharWithEscapedChar())));
|
|
}
|
|
}
|
|
|
|
if (Context.Dependencies.ConvertedStructs.Num())
|
|
{
|
|
Context.AddLine(TEXT("// List of all referenced converted structures"));
|
|
}
|
|
for (auto LocStruct : Context.Dependencies.ConvertedStructs)
|
|
{
|
|
if (ParentDependencies.IsValid() && ParentDependencies->ConvertedStructs.Contains(LocStruct))
|
|
{
|
|
continue;
|
|
}
|
|
const FString StructConstructor = FDependenciesHelper::GenerateZConstructor(LocStruct);
|
|
Context.AddLine(FString::Printf(TEXT("extern UScriptStruct* %s;"), *StructConstructor));
|
|
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);"), GET_MEMBER_NAME_STRING_CHECKED(UDynamicClass, ReferencedConvertedFields), *StructConstructor));
|
|
}
|
|
|
|
TArray<UActorComponent*> ActorComponentTempatesOwnedByClass = BPGC->ComponentTemplates;
|
|
// Gather all CT from SCS and IH, the remaining ones are generated for class..
|
|
if (auto SCS = BPGC->SimpleConstructionScript)
|
|
{
|
|
// >>> This code should be removed, once UE-39168 is fixed
|
|
//TODO: it's an ugly workaround - template from DefaultSceneRootNode is unnecessarily cooked :(
|
|
UActorComponent* DefaultSceneRootComponentTemplate = SCS->GetDefaultSceneRootNode() ? SCS->GetDefaultSceneRootNode()->ComponentTemplate : nullptr;
|
|
if (DefaultSceneRootComponentTemplate)
|
|
{
|
|
ActorComponentTempatesOwnedByClass.Add(DefaultSceneRootComponentTemplate);
|
|
}
|
|
// <<< This code should be removed, once UE-39168 is fixed
|
|
|
|
for (auto Node : SCS->GetAllNodes())
|
|
{
|
|
ActorComponentTempatesOwnedByClass.RemoveSwap(Node->ComponentTemplate);
|
|
}
|
|
}
|
|
if (auto IH = BPGC->GetInheritableComponentHandler())
|
|
{
|
|
TArray<UActorComponent*> AllTemplates;
|
|
IH->GetAllTemplates(AllTemplates);
|
|
ActorComponentTempatesOwnedByClass.RemoveAllSwap([&](UActorComponent* Component) -> bool
|
|
{
|
|
return AllTemplates.Contains(Component);
|
|
});
|
|
}
|
|
|
|
Context.AddLine(TEXT("FConvertedBlueprintsDependencies::FillUsedAssetsInDynamicClass(InDynamicClass, &__StaticDependencies_DirectlyUsedAssets);"));
|
|
|
|
auto CreateAndInitializeClassSubobjects = [&](bool bCreate, bool bInitialize)
|
|
{
|
|
for (auto ComponentTemplate : ActorComponentTempatesOwnedByClass)
|
|
{
|
|
if (ComponentTemplate)
|
|
{
|
|
HandleClassSubobject(Context, ComponentTemplate, FEmitterLocalContext::EClassSubobjectList::ComponentTemplates, bCreate, bInitialize);
|
|
}
|
|
}
|
|
|
|
for (auto TimelineTemplate : BPGC->Timelines)
|
|
{
|
|
if (TimelineTemplate)
|
|
{
|
|
HandleClassSubobject(Context, TimelineTemplate, FEmitterLocalContext::EClassSubobjectList::Timelines, bCreate, bInitialize);
|
|
}
|
|
}
|
|
|
|
for (auto DynamicBindingObject : BPGC->DynamicBindingObjects)
|
|
{
|
|
if (DynamicBindingObject)
|
|
{
|
|
HandleClassSubobject(Context, DynamicBindingObject, FEmitterLocalContext::EClassSubobjectList::DynamicBindingObjects, bCreate, bInitialize);
|
|
}
|
|
}
|
|
FBackendHelperUMG::CreateClassSubobjects(Context, bCreate, bInitialize);
|
|
};
|
|
CreateAndInitializeClassSubobjects(true, false);
|
|
CreateAndInitializeClassSubobjects(false, true);
|
|
|
|
FBackendHelperAnim::CreateAnimClassData(Context);
|
|
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
|
|
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::Regular;
|
|
Context.ResetPropertiesForInaccessibleStructs();
|
|
|
|
FBackendHelperUMG::EmitWidgetInitializationFunctions(Context);
|
|
}
|
|
|
|
void FEmitDefaultValueHelper::GenerateConstructor(FEmitterLocalContext& Context)
|
|
{
|
|
UBlueprintGeneratedClass* BPGC = CastChecked<UBlueprintGeneratedClass>(Context.GetCurrentlyGeneratedClass());
|
|
const FString CppClassName = FEmitHelper::GetCppName(BPGC);
|
|
|
|
UClass* SuperClass = BPGC->GetSuperClass();
|
|
const bool bSuperHasObjectInitializerConstructor = SuperClass && SuperClass->HasMetaData(TEXT("ObjectInitializerConstructorDeclared"));
|
|
|
|
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::CommonConstructor;
|
|
Context.ResetPropertiesForInaccessibleStructs();
|
|
Context.AddLine(FString::Printf(TEXT("%s::%s(const FObjectInitializer& ObjectInitializer) : Super(%s)")
|
|
, *CppClassName
|
|
, *CppClassName
|
|
, bSuperHasObjectInitializerConstructor ? TEXT("ObjectInitializer") : TEXT("")));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
|
|
// Call CustomDynamicClassInitialization
|
|
Context.AddLine(FString::Printf(TEXT("if(HasAnyFlags(RF_ClassDefaultObject) && (%s::StaticClass() == GetClass()))"), *CppClassName));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("%s::__CustomDynamicClassInitialization(CastChecked<UDynamicClass>(GetClass()));"), *CppClassName));
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
|
|
// Components that must be fixed after serialization
|
|
TArray<FString> NativeCreatedComponentProperties;
|
|
TArray<FNonativeComponentData> ComponentsToInit;
|
|
{
|
|
UObject* CDO = BPGC->GetDefaultObject(false);
|
|
|
|
UObject* ParentCDO = BPGC->GetSuperClass()->GetDefaultObject(false);
|
|
check(CDO && ParentCDO);
|
|
Context.AddLine(TEXT(""));
|
|
|
|
FString NativeRootComponentFallback;
|
|
TSet<const UProperty*> HandledProperties;
|
|
|
|
// Generate ctor init code for native class default subobjects that are always instanced (e.g. components).
|
|
// @TODO (pkavan) - We can probably make this faster by generating code to index through the DSO array instead (i.e. in place of HandleInstancedSubobject which will generate a lookup call per DSO).
|
|
TArray<UObject*> NativeDefaultObjectSubobjects;
|
|
BPGC->GetDefaultObjectSubobjects(NativeDefaultObjectSubobjects);
|
|
for (auto DSO : NativeDefaultObjectSubobjects)
|
|
{
|
|
if (DSO && DSO->GetClass()->HasAnyClassFlags(CLASS_DefaultToInstanced))
|
|
{
|
|
// Determine if this is an editor-only subobject.
|
|
bool bIsEditorOnlySubobject = false;
|
|
if (const UActorComponent* ActorComponent = Cast<UActorComponent>(DSO))
|
|
{
|
|
bIsEditorOnlySubobject = ActorComponent->IsEditorOnly();
|
|
}
|
|
|
|
// Skip ctor code gen for editor-only subobjects, since they won't be used by the runtime. Any dependencies on editor-only subobjects will be handled later (see HandleInstancedSubobject).
|
|
if (!bIsEditorOnlySubobject)
|
|
{
|
|
const FString VariableName = HandleInstancedSubobject(Context, DSO, false, true);
|
|
|
|
// Keep track of which component can be used as a root, in case it's not explicitly set.
|
|
if (NativeRootComponentFallback.IsEmpty())
|
|
{
|
|
USceneComponent* SceneComponent = Cast<USceneComponent>(DSO);
|
|
if (SceneComponent && !SceneComponent->GetAttachParent() && SceneComponent->CreationMethod == EComponentCreationMethod::Native)
|
|
{
|
|
NativeRootComponentFallback = VariableName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for a valid RootComponent property value; mark it as handled if already set in the defaults.
|
|
bool bNeedsRootComponentAssignment = false;
|
|
static const FName RootComponentPropertyName(TEXT("RootComponent"));
|
|
const UObjectProperty* RootComponentProperty = FindField<UObjectProperty>(BPGC, RootComponentPropertyName);
|
|
if (RootComponentProperty)
|
|
{
|
|
if (RootComponentProperty->GetObjectPropertyValue_InContainer(CDO))
|
|
{
|
|
HandledProperties.Add(RootComponentProperty);
|
|
}
|
|
else if (!NativeRootComponentFallback.IsEmpty())
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("RootComponent = %s;"), *NativeRootComponentFallback));
|
|
HandledProperties.Add(RootComponentProperty);
|
|
}
|
|
else
|
|
{
|
|
bNeedsRootComponentAssignment = true;
|
|
}
|
|
}
|
|
|
|
// Generate ctor init code for the SCS node hierarchy (i.e. non-native components). SCS nodes may have dependencies on native DSOs, but not vice-versa.
|
|
TArray<const UBlueprintGeneratedClass*> BPGCStack;
|
|
const bool bErrorFree = UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(BPGC, BPGCStack);
|
|
if (bErrorFree)
|
|
{
|
|
// Start at the base of the hierarchy so that dependencies are handled first.
|
|
for (int32 i = BPGCStack.Num() - 1; i >= 0; --i)
|
|
{
|
|
if (BPGCStack[i]->SimpleConstructionScript)
|
|
{
|
|
for (auto Node : BPGCStack[i]->SimpleConstructionScript->GetRootNodes())
|
|
{
|
|
if (Node)
|
|
{
|
|
const FString NativeVariablePropertyName = HandleNonNativeComponent(Context, Node, HandledProperties, NativeCreatedComponentProperties, nullptr, ComponentsToInit);
|
|
|
|
if (bNeedsRootComponentAssignment && Node->ComponentTemplate && Node->ComponentTemplate->IsA<USceneComponent>() && !NativeVariablePropertyName.IsEmpty())
|
|
{
|
|
// Only emit the explicit root component assignment statement if we're looking at the child BPGC that we're generating ctor code
|
|
// for. In all other cases, the root component will already be set up by a chained parent ctor call, so we avoid stomping it here.
|
|
if (i == 0)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("RootComponent = %s;"), *NativeVariablePropertyName));
|
|
HandledProperties.Add(RootComponentProperty);
|
|
}
|
|
|
|
bNeedsRootComponentAssignment = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& ComponentToInit : ComponentsToInit)
|
|
{
|
|
ComponentToInit.EmitProperties(Context);
|
|
|
|
if (Cast<UPrimitiveComponent>(ComponentToInit.ComponentTemplate))
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("if(!%s->%s())"), *ComponentToInit.NativeVariablePropertyName, GET_FUNCTION_NAME_STRING_CHECKED(UPrimitiveComponent, IsTemplate)));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s.%s(%s);")
|
|
, *ComponentToInit.NativeVariablePropertyName
|
|
, GET_MEMBER_NAME_STRING_CHECKED(UPrimitiveComponent, BodyInstance)
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(FBodyInstance, FixupData)
|
|
, *ComponentToInit.NativeVariablePropertyName));
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate ctor init code for generated Blueprint class property values that may differ from parent class defaults (or that otherwise belong to the generated Blueprint class).
|
|
for (auto Property : TFieldRange<const UProperty>(BPGC))
|
|
{
|
|
if (!HandledProperties.Contains(Property))
|
|
{
|
|
const bool bNewProperty = Property->GetOwnerStruct() == BPGC;
|
|
OuterGenerate(Context, Property, TEXT(""), reinterpret_cast<const uint8*>(CDO), bNewProperty ? nullptr : reinterpret_cast<const uint8*>(ParentCDO), EPropertyAccessOperator::None, true);
|
|
}
|
|
}
|
|
}
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
|
|
// TODO: this mechanism could be required by other instanced subobjects.
|
|
Context.CurrentCodeType = FEmitterLocalContext::EGeneratedCodeType::Regular;
|
|
Context.ResetPropertiesForInaccessibleStructs();
|
|
|
|
Context.ResetPropertiesForInaccessibleStructs();
|
|
Context.AddLine(FString::Printf(TEXT("void %s::%s(FObjectInstancingGraph* OuterInstanceGraph)"), *CppClassName, GET_FUNCTION_NAME_STRING_CHECKED(UObject, PostLoadSubobjects)));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("Super::%s(OuterInstanceGraph);"), GET_FUNCTION_NAME_STRING_CHECKED(UObject, PostLoadSubobjects)));
|
|
for (auto& ComponentToFix : NativeCreatedComponentProperties)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("if(%s)"), *ComponentToFix));
|
|
Context.AddLine(TEXT("{"));
|
|
Context.IncreaseIndent();
|
|
Context.AddLine(FString::Printf(TEXT("%s->%s = EComponentCreationMethod::Native;"), *ComponentToFix, GET_MEMBER_NAME_STRING_CHECKED(UActorComponent, CreationMethod)));
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
Context.DecreaseIndent();
|
|
Context.AddLine(TEXT("}"));
|
|
}
|
|
|
|
FString FEmitDefaultValueHelper::HandleClassSubobject(FEmitterLocalContext& Context, UObject* Object, FEmitterLocalContext::EClassSubobjectList ListOfSubobjectsType, bool bCreate, bool bInitialize)
|
|
{
|
|
ensure(Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass);
|
|
|
|
FString LocalNativeName;
|
|
if (bCreate)
|
|
{
|
|
FString OuterStr = Context.FindGloballyMappedObject(Object->GetOuter());
|
|
if (OuterStr.IsEmpty())
|
|
{
|
|
OuterStr = HandleClassSubobject(Context, Object->GetOuter(), ListOfSubobjectsType, bCreate, bInitialize);
|
|
if (OuterStr.IsEmpty())
|
|
{
|
|
return FString();
|
|
}
|
|
const FString AlreadyCreatedObject = Context.FindGloballyMappedObject(Object);
|
|
if (!AlreadyCreatedObject.IsEmpty())
|
|
{
|
|
return AlreadyCreatedObject;
|
|
}
|
|
}
|
|
|
|
const bool bAddAsSubobjectOfClass = Object->GetOuter() == Context.GetCurrentlyGeneratedClass();
|
|
LocalNativeName = Context.GenerateUniqueLocalName();
|
|
Context.AddClassSubObject_InConstructor(Object, LocalNativeName);
|
|
UClass* ObjectClass = Object->GetClass();
|
|
const FString ActualClass = Context.FindGloballyMappedObject(ObjectClass, UClass::StaticClass());
|
|
const FString NativeType = FEmitHelper::GetCppName(Context.GetFirstNativeOrConvertedClass(ObjectClass));
|
|
if(!ObjectClass->IsNative())
|
|
{
|
|
// make sure CDO has been created for NativeType:
|
|
Context.AddLine(FString::Printf(TEXT("%s::StaticClass()->GetDefaultObject();"), *NativeType));
|
|
}
|
|
Context.AddLine(FString::Printf(
|
|
TEXT("auto %s = NewObject<%s>(%s, %s, TEXT(\"%s\"));")
|
|
, *LocalNativeName
|
|
, *NativeType
|
|
, bAddAsSubobjectOfClass ? TEXT("InDynamicClass") : *OuterStr
|
|
, *ActualClass
|
|
, *Object->GetName().ReplaceCharWithEscapedChar()));
|
|
if (bAddAsSubobjectOfClass)
|
|
{
|
|
Context.RegisterClassSubobject(Object, ListOfSubobjectsType);
|
|
Context.AddLine(FString::Printf(TEXT("InDynamicClass->%s.Add(%s);")
|
|
, Context.ClassSubobjectListName(ListOfSubobjectsType)
|
|
, *LocalNativeName));
|
|
}
|
|
}
|
|
|
|
if (bInitialize)
|
|
{
|
|
if (LocalNativeName.IsEmpty())
|
|
{
|
|
LocalNativeName = Context.FindGloballyMappedObject(Object);
|
|
}
|
|
ensure(!LocalNativeName.IsEmpty());
|
|
auto CDO = Object->GetClass()->GetDefaultObject(false);
|
|
for (auto Property : TFieldRange<const UProperty>(Object->GetClass()))
|
|
{
|
|
OuterGenerate(Context, Property, LocalNativeName
|
|
, reinterpret_cast<const uint8*>(Object)
|
|
, reinterpret_cast<const uint8*>(CDO)
|
|
, EPropertyAccessOperator::Pointer);
|
|
}
|
|
}
|
|
return LocalNativeName;
|
|
}
|
|
|
|
FString FEmitDefaultValueHelper::HandleInstancedSubobject(FEmitterLocalContext& Context, UObject* Object, bool bCreateInstance, bool bSkipEditorOnlyCheck)
|
|
{
|
|
check(Object);
|
|
|
|
// Make sure we don't emit initialization code for the same object more than once.
|
|
FString LocalNativeName = Context.FindGloballyMappedObject(Object);
|
|
if (!LocalNativeName.IsEmpty())
|
|
{
|
|
return LocalNativeName;
|
|
}
|
|
else
|
|
{
|
|
LocalNativeName = Context.GenerateUniqueLocalName();
|
|
}
|
|
|
|
if (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::SubobjectsOfClass)
|
|
{
|
|
Context.AddClassSubObject_InConstructor(Object, LocalNativeName);
|
|
}
|
|
else if (Context.CurrentCodeType == FEmitterLocalContext::EGeneratedCodeType::CommonConstructor)
|
|
{
|
|
Context.AddCommonSubObject_InConstructor(Object, LocalNativeName);
|
|
}
|
|
|
|
UClass* ObjectClass = Object->GetClass();
|
|
|
|
// Determine if this is an editor-only subobject. When handling as a dependency, we'll create a "dummy" object in its place (below).
|
|
bool bIsEditorOnlySubobject = false;
|
|
if (!bSkipEditorOnlyCheck)
|
|
{
|
|
if (UActorComponent* ActorComponent = Cast<UActorComponent>(Object))
|
|
{
|
|
bIsEditorOnlySubobject = ActorComponent->IsEditorOnly();
|
|
if (bIsEditorOnlySubobject)
|
|
{
|
|
// Replace the potentially editor-only class with a base actor/scene component class that's available to the runtime. We'll create a "dummy" object of this type to stand in for the editor-only subobject below.
|
|
ObjectClass = ObjectClass->IsChildOf<USceneComponent>() ? USceneComponent::StaticClass() : UActorComponent::StaticClass();
|
|
}
|
|
}
|
|
}
|
|
|
|
auto BPGC = Context.GetCurrentlyGeneratedClass();
|
|
auto CDO = BPGC ? BPGC->GetDefaultObject(false) : nullptr;
|
|
if (!bIsEditorOnlySubobject && ensure(CDO) && (CDO == Object->GetOuter()))
|
|
{
|
|
if (bCreateInstance)
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("auto %s = CreateDefaultSubobject<%s>(TEXT(\"%s\"));")
|
|
, *LocalNativeName, *FEmitHelper::GetCppName(ObjectClass), *Object->GetName()));
|
|
}
|
|
else
|
|
{
|
|
Context.AddLine(FString::Printf(TEXT("auto %s = CastChecked<%s>(%s(TEXT(\"%s\")));")
|
|
, *LocalNativeName
|
|
, *FEmitHelper::GetCppName(ObjectClass)
|
|
, GET_FUNCTION_NAME_STRING_CHECKED(UObject, GetDefaultSubobjectByName)
|
|
, *Object->GetName()));
|
|
}
|
|
|
|
const UObject* ObjectArchetype = Object->GetArchetype();
|
|
for (auto Property : TFieldRange<const UProperty>(ObjectClass))
|
|
{
|
|
OuterGenerate(Context, Property, LocalNativeName
|
|
, reinterpret_cast<const uint8*>(Object)
|
|
, reinterpret_cast<const uint8*>(ObjectArchetype)
|
|
, EPropertyAccessOperator::Pointer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const FString OuterStr = Context.FindGloballyMappedObject(Object->GetOuter());
|
|
if (OuterStr.IsEmpty())
|
|
{
|
|
ensure(false);
|
|
return FString();
|
|
}
|
|
|
|
const FString ActualClass = Context.FindGloballyMappedObject(ObjectClass, UClass::StaticClass());
|
|
const FString NativeType = FEmitHelper::GetCppName(Context.GetFirstNativeOrConvertedClass(ObjectClass));
|
|
if(!ObjectClass->IsNative())
|
|
{
|
|
// make sure CDO has been created for NativeType:
|
|
Context.AddLine(FString::Printf(TEXT("%s::StaticClass()->GetDefaultObject();"), *NativeType));
|
|
}
|
|
Context.AddLine(FString::Printf(
|
|
TEXT("auto %s = NewObject<%s>(%s, %s, TEXT(\"%s\"));")
|
|
, *LocalNativeName
|
|
, *NativeType
|
|
, *OuterStr
|
|
, *ActualClass
|
|
, *Object->GetName().ReplaceCharWithEscapedChar()));
|
|
}
|
|
|
|
return LocalNativeName;
|
|
}
|