ue-mcp 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/action-class.js +56 -3
- package/dist/action-class.js.map +1 -1
- package/dist/bridge-timeouts.d.ts +64 -0
- package/dist/bridge-timeouts.js +118 -0
- package/dist/bridge-timeouts.js.map +1 -0
- package/dist/bridge.js +10 -2
- package/dist/bridge.js.map +1 -1
- package/dist/editor-control.d.ts +72 -5
- package/dist/editor-control.js +164 -140
- package/dist/editor-control.js.map +1 -1
- package/dist/editor-target.d.ts +55 -1
- package/dist/editor-target.js +107 -10
- package/dist/editor-target.js.map +1 -1
- package/dist/engine-observer.d.ts +59 -5
- package/dist/engine-observer.js +249 -29
- package/dist/engine-observer.js.map +1 -1
- package/dist/engine-root.d.ts +91 -0
- package/dist/engine-root.js +448 -0
- package/dist/engine-root.js.map +1 -0
- package/dist/epic-enrich.js +16 -1
- package/dist/epic-enrich.js.map +1 -1
- package/dist/feedback-cli.js +4 -0
- package/dist/feedback-cli.js.map +1 -1
- package/dist/feedback-deferred.d.ts +9 -0
- package/dist/feedback-deferred.js +2 -0
- package/dist/feedback-deferred.js.map +1 -1
- package/dist/feedback-fallback.d.ts +99 -0
- package/dist/feedback-fallback.js +173 -0
- package/dist/feedback-fallback.js.map +1 -0
- package/dist/flow/write-methods.js +20 -0
- package/dist/flow/write-methods.js.map +1 -1
- package/dist/index.js +41 -2
- package/dist/index.js.map +1 -1
- package/dist/lean-context.js +8 -3
- package/dist/lean-context.js.map +1 -1
- package/dist/locking.js +11 -2
- package/dist/locking.js.map +1 -1
- package/dist/plugin/loader.d.ts +4 -0
- package/dist/plugin/loader.js +22 -8
- package/dist/plugin/loader.js.map +1 -1
- package/dist/plugin/manifest.d.ts +72 -52
- package/dist/plugin/manifest.js +118 -21
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin-cli.js +16 -3
- package/dist/plugin-cli.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +23 -2
- package/dist/project.js.map +1 -1
- package/dist/python-gate.d.ts +95 -0
- package/dist/python-gate.js +175 -0
- package/dist/python-gate.js.map +1 -0
- package/dist/tool-counts.json +16 -16
- package/dist/tools/animation.js +288 -24
- package/dist/tools/animation.js.map +1 -1
- package/dist/tools/asset.js +79 -14
- package/dist/tools/asset.js.map +1 -1
- package/dist/tools/audio.js +59 -5
- package/dist/tools/audio.js.map +1 -1
- package/dist/tools/blueprint.js +29 -12
- package/dist/tools/blueprint.js.map +1 -1
- package/dist/tools/chooser.js +4 -1
- package/dist/tools/chooser.js.map +1 -1
- package/dist/tools/editor.js +81 -43
- package/dist/tools/editor.js.map +1 -1
- package/dist/tools/feedback.js +214 -48
- package/dist/tools/feedback.js.map +1 -1
- package/dist/tools/foliage.js +27 -1
- package/dist/tools/foliage.js.map +1 -1
- package/dist/tools/gameplay.js +38 -11
- package/dist/tools/gameplay.js.map +1 -1
- package/dist/tools/gas.js +17 -5
- package/dist/tools/gas.js.map +1 -1
- package/dist/tools/landscape.js +22 -7
- package/dist/tools/landscape.js.map +1 -1
- package/dist/tools/level.js +322 -51
- package/dist/tools/level.js.map +1 -1
- package/dist/tools/material.js +10 -4
- package/dist/tools/material.js.map +1 -1
- package/dist/tools/niagara.js +3 -2
- package/dist/tools/niagara.js.map +1 -1
- package/dist/tools/pcg.js +6 -5
- package/dist/tools/pcg.js.map +1 -1
- package/dist/tools/plugins.js +2 -0
- package/dist/tools/plugins.js.map +1 -1
- package/dist/tools/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.js +46 -3
- package/dist/types.js.map +1 -1
- package/dist/ue-mcp.default.yml +493 -125
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
- package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
- package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
- package/skills/ue-mcp-animation/SKILL.md +154 -0
- package/skills/ue-mcp-blueprint/SKILL.md +71 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
// Shared machinery for the UFUNCTION-invoking handlers (invoke_function,
|
|
4
|
+
// invoke_object_function, invoke_static_function).
|
|
5
|
+
//
|
|
6
|
+
// It lives in a header rather than a file-local helper in one of the handler
|
|
7
|
+
// .cpp files because the module is compiled as a unity build: two translation
|
|
8
|
+
// units that each defined a helper of this name would merge their anonymous
|
|
9
|
+
// namespaces and the second definition would be a redefinition.
|
|
10
|
+
|
|
11
|
+
#include "CoreMinimal.h"
|
|
12
|
+
#include "Containers/Ticker.h"
|
|
13
|
+
#include "Dom/JsonObject.h"
|
|
14
|
+
#include "Dom/JsonValue.h"
|
|
15
|
+
#include "UObject/Class.h"
|
|
16
|
+
#include "UObject/EnumProperty.h"
|
|
17
|
+
#include "UObject/Script.h"
|
|
18
|
+
#include "UObject/StrongObjectPtr.h"
|
|
19
|
+
#include "UObject/TextProperty.h"
|
|
20
|
+
#include "UObject/UnrealType.h"
|
|
21
|
+
#include "UObject/UObjectGlobals.h"
|
|
22
|
+
#include "UObject/WeakObjectPtr.h"
|
|
23
|
+
#include "UObject/WeakObjectPtrTemplates.h"
|
|
24
|
+
|
|
25
|
+
namespace MCPFunctionCall
|
|
26
|
+
{
|
|
27
|
+
/**
|
|
28
|
+
* Typed JSON for one value inside a container. Recursive, so a
|
|
29
|
+
* TArray<TArray<int32>> or a TMap<FName, TArray<FString>> comes back whole.
|
|
30
|
+
*/
|
|
31
|
+
inline TSharedPtr<FJsonValue> ValueToJson(FProperty* Prop, const void* ValuePtr, UObject* Parent);
|
|
32
|
+
|
|
33
|
+
/** Leaf marshalling for container elements: everything that is not a container. */
|
|
34
|
+
inline TSharedPtr<FJsonValue> ElementToJson(FProperty* Prop, const void* ValuePtr, UObject* Parent)
|
|
35
|
+
{
|
|
36
|
+
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
|
|
37
|
+
{
|
|
38
|
+
return MakeShared<FJsonValueBoolean>(BoolProp->GetPropertyValue(ValuePtr));
|
|
39
|
+
}
|
|
40
|
+
if (FStrProperty* StrProp = CastField<FStrProperty>(Prop))
|
|
41
|
+
{
|
|
42
|
+
return MakeShared<FJsonValueString>(StrProp->GetPropertyValue(ValuePtr));
|
|
43
|
+
}
|
|
44
|
+
if (FNameProperty* NameProp = CastField<FNameProperty>(Prop))
|
|
45
|
+
{
|
|
46
|
+
return MakeShared<FJsonValueString>(NameProp->GetPropertyValue(ValuePtr).ToString());
|
|
47
|
+
}
|
|
48
|
+
if (FTextProperty* TextProp = CastField<FTextProperty>(Prop))
|
|
49
|
+
{
|
|
50
|
+
return MakeShared<FJsonValueString>(TextProp->GetPropertyValue(ValuePtr).ToString());
|
|
51
|
+
}
|
|
52
|
+
// Enums report their name, not their ordinal: a bare number in a result
|
|
53
|
+
// is unreadable and a caller cannot map it back without the UEnum.
|
|
54
|
+
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
|
|
55
|
+
{
|
|
56
|
+
const int64 Raw = EnumProp->GetUnderlyingProperty()->GetSignedIntPropertyValue(ValuePtr);
|
|
57
|
+
if (UEnum* Enum = EnumProp->GetEnum())
|
|
58
|
+
{
|
|
59
|
+
return MakeShared<FJsonValueString>(Enum->GetNameStringByValue(Raw));
|
|
60
|
+
}
|
|
61
|
+
return MakeShared<FJsonValueNumber>(static_cast<double>(Raw));
|
|
62
|
+
}
|
|
63
|
+
if (FByteProperty* ByteProp = CastField<FByteProperty>(Prop))
|
|
64
|
+
{
|
|
65
|
+
const uint8 Raw = ByteProp->GetPropertyValue(ValuePtr);
|
|
66
|
+
if (UEnum* Enum = ByteProp->Enum)
|
|
67
|
+
{
|
|
68
|
+
return MakeShared<FJsonValueString>(Enum->GetNameStringByValue(static_cast<int64>(Raw)));
|
|
69
|
+
}
|
|
70
|
+
return MakeShared<FJsonValueNumber>(static_cast<double>(Raw));
|
|
71
|
+
}
|
|
72
|
+
if (FNumericProperty* NumProp = CastField<FNumericProperty>(Prop))
|
|
73
|
+
{
|
|
74
|
+
if (NumProp->IsFloatingPoint())
|
|
75
|
+
{
|
|
76
|
+
return MakeShared<FJsonValueNumber>(NumProp->GetFloatingPointPropertyValue(ValuePtr));
|
|
77
|
+
}
|
|
78
|
+
return MakeShared<FJsonValueNumber>(static_cast<double>(NumProp->GetSignedIntPropertyValue(ValuePtr)));
|
|
79
|
+
}
|
|
80
|
+
// Soft references before hard ones: a soft pointer derives from
|
|
81
|
+
// FObjectPropertyBase but answers null while its target is unloaded,
|
|
82
|
+
// which would report a live reference as None (#598).
|
|
83
|
+
if (FSoftObjectProperty* SoftProp = CastField<FSoftObjectProperty>(Prop))
|
|
84
|
+
{
|
|
85
|
+
const FString Path = SoftProp->GetPropertyValue(ValuePtr).ToString();
|
|
86
|
+
return MakeShared<FJsonValueString>(Path.IsEmpty() ? FString(TEXT("None")) : Path);
|
|
87
|
+
}
|
|
88
|
+
if (FObjectPropertyBase* ObjProp = CastField<FObjectPropertyBase>(Prop))
|
|
89
|
+
{
|
|
90
|
+
UObject* Referenced = ObjProp->GetObjectPropertyValue(ValuePtr);
|
|
91
|
+
if (!Referenced) return MakeShared<FJsonValueString>(TEXT("None"));
|
|
92
|
+
// The frame is raw bytes and invisible to GC, so an object the call
|
|
93
|
+
// destroyed would be dereferenced by the path read below.
|
|
94
|
+
if (!IsValid(Referenced)) return MakeShared<FJsonValueString>(TEXT("(collected during the call)"));
|
|
95
|
+
return MakeShared<FJsonValueString>(Referenced->GetPathName());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Structs and anything else: export text, the spelling every other
|
|
99
|
+
// return value in this response already uses.
|
|
100
|
+
FString Exported;
|
|
101
|
+
Prop->ExportTextItem_Direct(Exported, ValuePtr, nullptr, Parent, PPF_None);
|
|
102
|
+
return MakeShared<FJsonValueString>(Exported);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
inline TSharedPtr<FJsonValue> ValueToJson(FProperty* Prop, const void* ValuePtr, UObject* Parent)
|
|
106
|
+
{
|
|
107
|
+
if (!Prop || !ValuePtr) return MakeShared<FJsonValueNull>();
|
|
108
|
+
|
|
109
|
+
if (FArrayProperty* ArrayProp = CastField<FArrayProperty>(Prop))
|
|
110
|
+
{
|
|
111
|
+
FScriptArrayHelper Helper(ArrayProp, ValuePtr);
|
|
112
|
+
const int32 Count = Helper.Num();
|
|
113
|
+
TArray<TSharedPtr<FJsonValue>> Items;
|
|
114
|
+
Items.Reserve(Count);
|
|
115
|
+
for (int32 Index = 0; Index < Count; ++Index)
|
|
116
|
+
{
|
|
117
|
+
Items.Add(ValueToJson(ArrayProp->Inner, Helper.GetRawPtr(Index), Parent));
|
|
118
|
+
}
|
|
119
|
+
return MakeShared<FJsonValueArray>(Items);
|
|
120
|
+
}
|
|
121
|
+
if (FSetProperty* SetProp = CastField<FSetProperty>(Prop))
|
|
122
|
+
{
|
|
123
|
+
FScriptSetHelper Helper(SetProp, ValuePtr);
|
|
124
|
+
TArray<TSharedPtr<FJsonValue>> Items;
|
|
125
|
+
Items.Reserve(Helper.Num());
|
|
126
|
+
for (int32 Index = 0, MaxIndex = Helper.GetMaxIndex(); Index < MaxIndex; ++Index)
|
|
127
|
+
{
|
|
128
|
+
if (!Helper.IsValidIndex(Index)) continue;
|
|
129
|
+
Items.Add(ValueToJson(SetProp->ElementProp, Helper.GetElementPtr(Index), Parent));
|
|
130
|
+
}
|
|
131
|
+
return MakeShared<FJsonValueArray>(Items);
|
|
132
|
+
}
|
|
133
|
+
if (FMapProperty* MapProp = CastField<FMapProperty>(Prop))
|
|
134
|
+
{
|
|
135
|
+
// An array of {key,value} rather than a JSON object: a TMap key can
|
|
136
|
+
// be a struct or an enum, and collapsing it into an object key would
|
|
137
|
+
// lose that.
|
|
138
|
+
FScriptMapHelper Helper(MapProp, ValuePtr);
|
|
139
|
+
TArray<TSharedPtr<FJsonValue>> Pairs;
|
|
140
|
+
Pairs.Reserve(Helper.Num());
|
|
141
|
+
for (int32 Index = 0, MaxIndex = Helper.GetMaxIndex(); Index < MaxIndex; ++Index)
|
|
142
|
+
{
|
|
143
|
+
if (!Helper.IsValidIndex(Index)) continue;
|
|
144
|
+
TSharedPtr<FJsonObject> Pair = MakeShared<FJsonObject>();
|
|
145
|
+
Pair->SetField(TEXT("key"), ValueToJson(MapProp->KeyProp, Helper.GetKeyPtr(Index), Parent));
|
|
146
|
+
Pair->SetField(TEXT("value"), ValueToJson(MapProp->ValueProp, Helper.GetValuePtr(Index), Parent));
|
|
147
|
+
Pairs.Add(MakeShared<FJsonValueObject>(Pair));
|
|
148
|
+
}
|
|
149
|
+
return MakeShared<FJsonValueArray>(Pairs);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return ElementToJson(Prop, ValuePtr, Parent);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** True for the three property kinds export text cannot carry back usefully. */
|
|
156
|
+
inline bool IsContainerProperty(FProperty* Prop)
|
|
157
|
+
{
|
|
158
|
+
return Prop && (Prop->IsA<FArrayProperty>() || Prop->IsA<FSetProperty>() || Prop->IsA<FMapProperty>());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* #885: marshal one return or out parameter.
|
|
163
|
+
*
|
|
164
|
+
* A container becomes real JSON. Everything else keeps the export-text
|
|
165
|
+
* spelling every existing reader of returnValues parses, so this fixes the
|
|
166
|
+
* reported defect without moving the wire format underneath anyone: a
|
|
167
|
+
* TArray<FString> return arrived as an empty string, which made every
|
|
168
|
+
* array-returning accessor unreadable through the bridge.
|
|
169
|
+
*/
|
|
170
|
+
inline TSharedPtr<FJsonValue> OutputToJson(FProperty* Prop, const void* ValuePtr, UObject* Parent)
|
|
171
|
+
{
|
|
172
|
+
if (!Prop || !ValuePtr) return MakeShared<FJsonValueNull>();
|
|
173
|
+
if (IsContainerProperty(Prop)) return ValueToJson(Prop, ValuePtr, Parent);
|
|
174
|
+
|
|
175
|
+
// Object out-params are checked before export: the frame is raw bytes
|
|
176
|
+
// and invisible to GC, so exporting one the call destroyed would
|
|
177
|
+
// dereference freed memory. "(collected during the call)" is distinct
|
|
178
|
+
// from "None" on purpose - reporting both as empty reads as a null
|
|
179
|
+
// return.
|
|
180
|
+
if (FObjectPropertyBase* ObjProp = CastField<FObjectPropertyBase>(Prop))
|
|
181
|
+
{
|
|
182
|
+
UObject* Referenced = ObjProp->GetObjectPropertyValue(ValuePtr);
|
|
183
|
+
if (!Referenced) return MakeShared<FJsonValueString>(TEXT("None"));
|
|
184
|
+
if (!IsValid(Referenced)) return MakeShared<FJsonValueString>(TEXT("(collected during the call)"));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
FString Exported;
|
|
188
|
+
Prop->ExportTextItem_Direct(Exported, ValuePtr, nullptr, Parent, PPF_None);
|
|
189
|
+
return MakeShared<FJsonValueString>(Exported);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Read every return and out parameter of a called function out of its frame
|
|
194
|
+
* and onto OutVals. One implementation for all the invoke actions, so the
|
|
195
|
+
* wire format cannot diverge between them.
|
|
196
|
+
*/
|
|
197
|
+
inline void WriteOutputs(const TSharedPtr<FJsonObject>& OutVals, UFunction* Func, void* ParamBuf, UObject* Parent)
|
|
198
|
+
{
|
|
199
|
+
if (!OutVals.IsValid() || !Func || !ParamBuf) return;
|
|
200
|
+
for (TFieldIterator<FProperty> It(Func); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
201
|
+
{
|
|
202
|
+
FProperty* Prop = *It;
|
|
203
|
+
if (!(Prop->PropertyFlags & (CPF_ReturnParm | CPF_OutParm))) continue;
|
|
204
|
+
OutVals->SetField(Prop->GetName(), OutputToJson(Prop, Prop->ContainerPtrToValuePtr<void>(ParamBuf), Parent));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Destroy every parameter value in a frame that InitializeValue_InContainer built. */
|
|
209
|
+
inline void DestroyFrame(UFunction* Func, void* ParamBuf)
|
|
210
|
+
{
|
|
211
|
+
if (!Func || !ParamBuf) return;
|
|
212
|
+
for (TFieldIterator<FProperty> It(Func); It && (It->PropertyFlags & CPF_Parm); ++It)
|
|
213
|
+
{
|
|
214
|
+
It->DestroyValue_InContainer(ParamBuf);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── Dotted-path call syntax (#969) ───────────────────────────────────────
|
|
219
|
+
//
|
|
220
|
+
// get_runtime_values evaluates dotted paths per matched actor or component.
|
|
221
|
+
// A read-only accessor keyed by an id (a tally by option id, a balance by
|
|
222
|
+
// currency id, an attribute by tag) is a very common shape and was
|
|
223
|
+
// unreachable, because only a zero-arg UFUNCTION was accepted. These parse
|
|
224
|
+
// the "Name(literal, literal)" form the old error message already implied
|
|
225
|
+
// was being read.
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Split a dotted path into segments without breaking an argument list.
|
|
229
|
+
* "Comp.GetWeight(1.5)" is two segments, not three: the dot inside the
|
|
230
|
+
* parentheses belongs to the float literal. Empty segments are dropped,
|
|
231
|
+
* which is what ParseIntoArray did before this existed.
|
|
232
|
+
*/
|
|
233
|
+
inline void SplitPathSegments(const FString& Path, TArray<FString>& OutSegments)
|
|
234
|
+
{
|
|
235
|
+
OutSegments.Reset();
|
|
236
|
+
int32 Depth = 0;
|
|
237
|
+
TCHAR Quote = 0;
|
|
238
|
+
FString Current;
|
|
239
|
+
for (int32 Index = 0; Index < Path.Len(); ++Index)
|
|
240
|
+
{
|
|
241
|
+
const TCHAR Ch = Path[Index];
|
|
242
|
+
if (Quote != 0)
|
|
243
|
+
{
|
|
244
|
+
Current.AppendChar(Ch);
|
|
245
|
+
if (Ch == Quote) Quote = 0;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (Ch == TEXT('"') || Ch == TEXT('\''))
|
|
249
|
+
{
|
|
250
|
+
Quote = Ch;
|
|
251
|
+
Current.AppendChar(Ch);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (Ch == TEXT('(') || Ch == TEXT('[') || Ch == TEXT('{'))
|
|
255
|
+
{
|
|
256
|
+
++Depth;
|
|
257
|
+
Current.AppendChar(Ch);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (Ch == TEXT(')') || Ch == TEXT(']') || Ch == TEXT('}'))
|
|
261
|
+
{
|
|
262
|
+
if (Depth > 0) --Depth;
|
|
263
|
+
Current.AppendChar(Ch);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (Ch == TEXT('.') && Depth == 0)
|
|
267
|
+
{
|
|
268
|
+
OutSegments.Add(Current.TrimStartAndEnd());
|
|
269
|
+
Current.Reset();
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
Current.AppendChar(Ch);
|
|
273
|
+
}
|
|
274
|
+
OutSegments.Add(Current.TrimStartAndEnd());
|
|
275
|
+
OutSegments.RemoveAll([](const FString& Segment) { return Segment.IsEmpty(); });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Strip one level of matching quotes from a literal argument. Everything
|
|
280
|
+
* else is handed on verbatim: the value is coerced by the same property
|
|
281
|
+
* setter invoke_object_function's args go through, which already reads a
|
|
282
|
+
* string into an FName, an FString, a number, a bool or an enum.
|
|
283
|
+
*/
|
|
284
|
+
inline FString UnquoteArgLiteral(const FString& Literal)
|
|
285
|
+
{
|
|
286
|
+
const FString Trimmed = Literal.TrimStartAndEnd();
|
|
287
|
+
if (Trimmed.Len() >= 2)
|
|
288
|
+
{
|
|
289
|
+
const TCHAR First = Trimmed[0];
|
|
290
|
+
if ((First == TEXT('"') || First == TEXT('\'')) && Trimmed[Trimmed.Len() - 1] == First)
|
|
291
|
+
{
|
|
292
|
+
return Trimmed.Mid(1, Trimmed.Len() - 2);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return Trimmed;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Read "Name(a, b)" into its name and its raw argument literals.
|
|
300
|
+
*
|
|
301
|
+
* bOutHasArgList separates "Name" (no list, the historical zero-arg form)
|
|
302
|
+
* from "Name()" (an explicitly empty list), so a caller can keep answering
|
|
303
|
+
* the old shape exactly as before. Returns false with OutError set when the
|
|
304
|
+
* parentheses do not balance or an argument is empty, because a silently
|
|
305
|
+
* mis-parsed key would read back somebody else's value.
|
|
306
|
+
*/
|
|
307
|
+
inline bool ParseCallSegment(
|
|
308
|
+
const FString& Segment,
|
|
309
|
+
FString& OutName,
|
|
310
|
+
TArray<FString>& OutArgs,
|
|
311
|
+
bool& bOutHasArgList,
|
|
312
|
+
FString& OutError)
|
|
313
|
+
{
|
|
314
|
+
OutName = Segment.TrimStartAndEnd();
|
|
315
|
+
OutArgs.Reset();
|
|
316
|
+
bOutHasArgList = false;
|
|
317
|
+
OutError.Reset();
|
|
318
|
+
|
|
319
|
+
int32 Open = INDEX_NONE;
|
|
320
|
+
if (!Segment.FindChar(TEXT('('), Open))
|
|
321
|
+
{
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
int32 Depth = 0;
|
|
326
|
+
TCHAR Quote = 0;
|
|
327
|
+
int32 Close = INDEX_NONE;
|
|
328
|
+
for (int32 Index = Open; Index < Segment.Len(); ++Index)
|
|
329
|
+
{
|
|
330
|
+
const TCHAR Ch = Segment[Index];
|
|
331
|
+
if (Quote != 0)
|
|
332
|
+
{
|
|
333
|
+
if (Ch == Quote) Quote = 0;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (Ch == TEXT('"') || Ch == TEXT('\'')) { Quote = Ch; continue; }
|
|
337
|
+
if (Ch == TEXT('(')) { ++Depth; continue; }
|
|
338
|
+
if (Ch == TEXT(')') && --Depth == 0) { Close = Index; break; }
|
|
339
|
+
}
|
|
340
|
+
if (Close != Segment.Len() - 1)
|
|
341
|
+
{
|
|
342
|
+
OutError = FString::Printf(
|
|
343
|
+
TEXT("'%s' opens an argument list that does not close at the end of the segment"), *Segment);
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
OutName = Segment.Left(Open).TrimStartAndEnd();
|
|
348
|
+
if (OutName.IsEmpty())
|
|
349
|
+
{
|
|
350
|
+
OutError = FString::Printf(TEXT("'%s' has an argument list with no function name in front of it"), *Segment);
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
bOutHasArgList = true;
|
|
354
|
+
|
|
355
|
+
const FString Inner = Segment.Mid(Open + 1, Close - Open - 1);
|
|
356
|
+
if (Inner.TrimStartAndEnd().IsEmpty())
|
|
357
|
+
{
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
Depth = 0;
|
|
362
|
+
Quote = 0;
|
|
363
|
+
FString Current;
|
|
364
|
+
for (int32 Index = 0; Index < Inner.Len(); ++Index)
|
|
365
|
+
{
|
|
366
|
+
const TCHAR Ch = Inner[Index];
|
|
367
|
+
if (Quote != 0)
|
|
368
|
+
{
|
|
369
|
+
Current.AppendChar(Ch);
|
|
370
|
+
if (Ch == Quote) Quote = 0;
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (Ch == TEXT('"') || Ch == TEXT('\'')) { Quote = Ch; Current.AppendChar(Ch); continue; }
|
|
374
|
+
if (Ch == TEXT('(') || Ch == TEXT('[') || Ch == TEXT('{')) { ++Depth; Current.AppendChar(Ch); continue; }
|
|
375
|
+
if (Ch == TEXT(')') || Ch == TEXT(']') || Ch == TEXT('}')) { if (Depth > 0) --Depth; Current.AppendChar(Ch); continue; }
|
|
376
|
+
if (Ch == TEXT(',') && Depth == 0)
|
|
377
|
+
{
|
|
378
|
+
OutArgs.Add(Current);
|
|
379
|
+
Current.Reset();
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
Current.AppendChar(Ch);
|
|
383
|
+
}
|
|
384
|
+
OutArgs.Add(Current);
|
|
385
|
+
|
|
386
|
+
for (int32 Index = 0; Index < OutArgs.Num(); ++Index)
|
|
387
|
+
{
|
|
388
|
+
OutArgs[Index] = UnquoteArgLiteral(OutArgs[Index]);
|
|
389
|
+
if (OutArgs[Index].IsEmpty())
|
|
390
|
+
{
|
|
391
|
+
OutError = FString::Printf(TEXT("'%s' has an empty argument at position %d"), *Segment, Index + 1);
|
|
392
|
+
OutArgs.Reset();
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Render a callspace, which is a bitmask and can name more than one space. */
|
|
400
|
+
inline FString DescribeCallspace(int32 Callspace)
|
|
401
|
+
{
|
|
402
|
+
if (Callspace == FunctionCallspace::Absorbed) return TEXT("Absorbed");
|
|
403
|
+
TArray<FString> Parts;
|
|
404
|
+
if (Callspace & FunctionCallspace::Local) Parts.Add(TEXT("Local"));
|
|
405
|
+
if (Callspace & FunctionCallspace::Remote) Parts.Add(TEXT("Remote"));
|
|
406
|
+
if (Parts.Num() == 0) return FString::Printf(TEXT("Unknown(%d)"), Callspace);
|
|
407
|
+
return FString::Join(Parts, TEXT("+"));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* #973: report a network call whose routing the editor script guard is about
|
|
412
|
+
* to override.
|
|
413
|
+
*
|
|
414
|
+
* FEditorScriptExecutionGuard sets GAllowActorScriptExecutionInEditor, and
|
|
415
|
+
* AActor::GetFunctionCallspace checks that global in its FIRST branch and
|
|
416
|
+
* answers Local unconditionally. So a correct UFUNCTION(Server) reached from
|
|
417
|
+
* a scripted call runs the server implementation locally on a client copy
|
|
418
|
+
* instead of being sent, which looks exactly like a broken RPC.
|
|
419
|
+
*
|
|
420
|
+
* Call this BEFORE opening the guard: that is the only moment the honest
|
|
421
|
+
* callspace is observable. Returns true when the function is a net function
|
|
422
|
+
* whose callspace is about to be forced to Local from something else, and
|
|
423
|
+
* writes the callspace that would otherwise have applied.
|
|
424
|
+
*/
|
|
425
|
+
inline bool WouldForceNetCallspaceLocal(UObject* Target, UFunction* Func, FString& OutNaturalCallspace)
|
|
426
|
+
{
|
|
427
|
+
OutNaturalCallspace.Reset();
|
|
428
|
+
if (!Target || !Func) return false;
|
|
429
|
+
if (!Func->HasAnyFunctionFlags(FUNC_Net)) return false;
|
|
430
|
+
|
|
431
|
+
const int32 Callspace = Target->GetFunctionCallspace(Func, nullptr);
|
|
432
|
+
if (Callspace == FunctionCallspace::Local) return false;
|
|
433
|
+
OutNaturalCallspace = DescribeCallspace(Callspace);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Wording for the case above, so a false negative is visible in the result. */
|
|
438
|
+
inline FString DescribeForcedLocalCallspace(const FString& FunctionName, const FString& NaturalCallspace)
|
|
439
|
+
{
|
|
440
|
+
return FString::Printf(
|
|
441
|
+
TEXT("'%s' is a replicated UFUNCTION whose callspace here is %s, but a scripted call runs under the editor ")
|
|
442
|
+
TEXT("script-execution guard, which forces every actor callspace to Local. The implementation ran locally ")
|
|
443
|
+
TEXT("rather than being sent, so a refusal or a no-op here is not evidence that the RPC is broken. Pass ")
|
|
444
|
+
TEXT("deferToNextTick=true to run the call on the next engine tick, outside the guard, where it routes normally."),
|
|
445
|
+
*FunctionName, *NaturalCallspace);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* #973: run the call on the next engine tick instead of inline.
|
|
450
|
+
*
|
|
451
|
+
* The guard's scope is the handler, so a call queued here happens after that
|
|
452
|
+
* scope has ended and GetFunctionCallspace answers honestly. Takes ownership
|
|
453
|
+
* of the already-marshalled parameter frame; the frame is destroyed after the
|
|
454
|
+
* call, or without calling if the target has gone by then.
|
|
455
|
+
*
|
|
456
|
+
* Nothing can be read back: the response is written before the call runs.
|
|
457
|
+
* That is the trade a caller opts into by asking for the deferral.
|
|
458
|
+
*/
|
|
459
|
+
inline void DeferProcessEventToNextTick(UObject* Target, UFunction* Func, TArray<uint8>&& ParamBuf)
|
|
460
|
+
{
|
|
461
|
+
if (!Target || !Func) return;
|
|
462
|
+
|
|
463
|
+
TWeakObjectPtr<UObject> WeakTarget(Target);
|
|
464
|
+
|
|
465
|
+
// The frame is a raw byte buffer, so the garbage collector cannot see
|
|
466
|
+
// any UObject argument marshalled into it. Between this handler
|
|
467
|
+
// returning and the ticker firing there is a real window for an
|
|
468
|
+
// incremental GC, and a collected argument would be handed to
|
|
469
|
+
// ProcessEvent as a dangling pointer. Hold every object argument
|
|
470
|
+
// strongly for exactly that window.
|
|
471
|
+
TArray<TStrongObjectPtr<UObject>> HeldArgs;
|
|
472
|
+
for (TFieldIterator<FProperty> It(Func); It && It->HasAnyPropertyFlags(CPF_Parm); ++It)
|
|
473
|
+
{
|
|
474
|
+
if (It->HasAnyPropertyFlags(CPF_ReturnParm)) continue;
|
|
475
|
+
if (const FObjectPropertyBase* ObjProp = CastField<FObjectPropertyBase>(*It))
|
|
476
|
+
{
|
|
477
|
+
if (UObject* Arg = ObjProp->GetObjectPropertyValue(ObjProp->ContainerPtrToValuePtr<void>(ParamBuf.GetData())))
|
|
478
|
+
{
|
|
479
|
+
HeldArgs.Emplace(Arg);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// The UFunction is held strongly rather than weakly because it is what
|
|
485
|
+
// DestroyFrame walks to destruct the frame. If it were allowed to go
|
|
486
|
+
// (a Blueprint recompiled between queue and tick), every FString and
|
|
487
|
+
// TArray parameter in the buffer would leak instead.
|
|
488
|
+
TStrongObjectPtr<UFunction> HeldFunc(Func);
|
|
489
|
+
TSharedRef<TArray<uint8>> Frame = MakeShared<TArray<uint8>>(MoveTemp(ParamBuf));
|
|
490
|
+
|
|
491
|
+
FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda(
|
|
492
|
+
[WeakTarget, HeldFunc, HeldArgs, Frame](float) -> bool
|
|
493
|
+
{
|
|
494
|
+
UFunction* LiveFunc = HeldFunc.Get();
|
|
495
|
+
UObject* LiveTarget = WeakTarget.Get();
|
|
496
|
+
if (LiveTarget && LiveFunc && IsValid(LiveTarget))
|
|
497
|
+
{
|
|
498
|
+
// Deliberately NOT under FEditorScriptExecutionGuard: the
|
|
499
|
+
// whole point of the deferral is that the guard is closed.
|
|
500
|
+
LiveTarget->ProcessEvent(LiveFunc, Frame->GetData());
|
|
501
|
+
}
|
|
502
|
+
if (LiveFunc)
|
|
503
|
+
{
|
|
504
|
+
DestroyFrame(LiveFunc, Frame->GetData());
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}), 0.0f);
|
|
508
|
+
}
|
|
509
|
+
}
|