@aeonsparx/pi-unreal 0.0.0-stage → 1.1.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.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: ue-engine-resolution
3
+ description: Resolve which Unreal Engine installation a .uproject actually uses, and read .uproject/.uplugin facts from engine-authored schemas. Use when an engine path, version, plugin availability, or descriptor field must be established as fact, before UE builds, plugin edits, or any "which engine version is this?" question.
4
+ compatibility: Windows-first (registry via pwsh); resolver core is platform-neutral.
5
+ ---
6
+
7
+ # Unreal Engine resolution
8
+
9
+ Never infer an engine root from a directory name, a `UE_*` folder, or an environment variable alone.
10
+ Resolve it, then quote the resolved value.
11
+
12
+ ## Order of truth
13
+
14
+ 1. Run `unreal_resolve_engine` (or `/unreal`) first. It reports `engineRoot`, `identifier`,
15
+ `Build.version`, source rank, and — on failure — the rejected candidates with reasons.
16
+ 2. Only after a resolution exists should a build command, a `.Build.cs` read, or an
17
+ engine-source lookup name a concrete engine path.
18
+
19
+ Resolution mirrors the engine's own algorithm
20
+ (`Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp:459-511`, UE 5.8):
21
+
22
+ | Association in `.uproject` | Meaning | Engine source of truth |
23
+ | --- | --- | --- |
24
+ | `5.8` | installed (Launcher) build | `HKLM:\SOFTWARE\EpicGames\Unreal Engine\5.8` → `InstalledDirectory`, plus `<installDirDefault>\UE_5.8` |
25
+ | `{GUID}` | source build | `HKCU:\SOFTWARE\Epic Games\Unreal Engine\Builds` → root path |
26
+ | engine-root **path** | explicit pin, bypasses registration | validated as `<root>/Engine/Binaries` + `<root>/Engine/Build` |
27
+ | **empty** | project is inside an engine tree | walk up 12 levels for `Engine/Config/BaseEngine.ini` |
28
+
29
+ An association that matches more than one valid root is reported **ambiguous** — the engine
30
+ refuses to pick, so neither should the agent. Pass `engineRootHint` to disambiguate.
31
+
32
+ ## Machine-local facts that are not inferable
33
+
34
+ - `HKLM` registers UE 4.0 … 5.7. **UE 5.8 has no registry key**: it is found by scanning
35
+ `C:/Program Files/Epic Games` for `UE_*`, or by `$env:UNREAL_ENGINE`, which points at it.
36
+ - 5.5 and 5.6 registry entries point at directories that do not exist. A registry hit is a
37
+ **candidate**, never a conclusion — every candidate is validated
38
+ (`Engine/Binaries` + `Engine/Build` + `Engine/Build/Build.version` major.minor match)
39
+ before it can be reported.
40
+ - `D:/UnrealEngine/Source` is a source build (5.7.4) registered under a GUID in `HKCU`.
41
+
42
+ ## Descriptor fields: read them from the parser, not from memory
43
+
44
+ Field names, host types, and loading phases come from the engine's own serializers, not from
45
+ blog posts. `unreal_project_inspect` / `unreal_plugin_inspect` report the engine's names:
46
+
47
+ - `.uproject` → `Engine/Source/Runtime/Projects/Private/ProjectDescriptor.cpp:100-225`
48
+ - `.uplugin` → `Engine/Source/Runtime/Projects/Private/PluginDescriptor.cpp:263-409`
49
+ - module entries → `Engine/Source/Runtime/Projects/Private/ModuleDescriptor.cpp`
50
+ (`Type` is **required**; `EHostType::FromString` → `Max` → descriptor load fails)
51
+ - plugin references → `Engine/Source/Runtime/Projects/Private/PluginReferenceDescriptor.cpp:130-180`
52
+ (`Enabled` is **required**; a reference without it is rejected)
53
+
54
+ Host types (`EHostType::ToString`) and phases (`ELoadingPhase`) are enumerated by the parser;
55
+ an unrecognized value is reported with the engine's own failure text. Do not "fix" a descriptor
56
+ by inventing a key name — fix it to the key the engine reads.
57
+
58
+ `.uplugin` has **no** `Enabled` key. Enablement is `EnabledByDefault`
59
+ (`PluginManager.cpp:421-431`) plus the project's `"Enabled": true` reference.
60
+
61
+ ## Plugin availability
62
+
63
+ `unreal_project_inspect` resolves every reference against an index built in
64
+ UnrealBuildTool's discovery order (`Engine/Source/Programs/UnrealBuildTool/System/Plugins.cs:322`):
65
+ Engine → Project (`Plugins/`) → Mod (`Mods/`) → `AdditionalPluginDirectories`, with later scans
66
+ shadowing earlier ones on name collision. Use the reported status (`resolved`, `missing`,
67
+ `disabled`, `version-warning`, `shadowed`, `no-modules`) instead of assuming a plugin is
68
+ available. `Binaries/`, `Intermediate/`, `Saved/`, and `ThirdParty/` are never scanned.
69
+
70
+ Two facts are separate and both matter, and the report keeps them apart:
71
+
72
+ - **Reference status**: `Mover: resolved ← Engine · modules Mover(Runtime), MoverCVDData(Runtime),
73
+ MoverEditor(UncookedOnly)`. The modules are what the reference buys; "resolved" alone never said
74
+ which C++ exists.
75
+ - **Availability without a reference**: engine plugins with `EnabledByDefault: true` load even when
76
+ the `.uproject` does not list them, and a module can be inside the target's link graph without the
77
+ plugin being referenced. `unreal_context` with `module: "PhysicsControl"` reports the graph check,
78
+ the module directory, the declared `Type`, and the `.uproject` status as separate lines — "the
79
+ project lacks plugin X" is not "module X is unavailable", and not "X does not exist".
80
+
81
+ ## After resolution: where declarations and build context come from
82
+
83
+ `unreal_resolve_engine` answers *which engine*. The rest of the navigation questions are answered
84
+ by the index tools, and they use the resolved root — a wrong engine root means a wrong index:
85
+
86
+ - `unreal_symbol` — declaration site + owning module for a type, function, `UPROPERTY`, delegate.
87
+ Engine scope is opt-in (`scope: "engine"`). Its roots are `Engine/Source/<area>` **plus the plugin
88
+ roots the `.uproject` enables** — in an installed engine most gameplay types ship as plugins
89
+ (`Mover`, `PoseSearch`, `SmartObjects`, `BlendStack`, `StateTree`, `GameplayAbilities`), so an
90
+ index of `Engine/Source/Runtime` alone reports 0 rows for `UMoverComponent`. Plugins the project
91
+ does not reference are opt-in too: `engineAreas: ["Plugins"]` (every `Engine/Plugins` source,
92
+ ~2.8× the `Runtime` scan) or `engineAreas: ["Plugins/Animation/PhysicsControl"]` (one plugin).
93
+ - **A 0-row answer is about the roots scanned, not about the engine.** It prints the index keys, the
94
+ root kinds (`279 engine-source root(s), 62 engine-plugin root(s)`) and the next call. Run
95
+ `unreal_index` with `action: "status"` before trusting a 0: an index key encodes its root set, so
96
+ an index you did not build is not what was searched.
97
+ - `unreal_index` with `action: "headers"` — "does this header exist in this engine version?" A
98
+ path-only index (no content read, seconds, ~38 000 header paths on a UE 5.8 install): exact path,
99
+ owning module, owning plugin, `EnabledByDefault`, `.uproject` reference. Invented names return
100
+ *not a file path* plus the closest real **file** names — a file name is not a type, and a token
101
+ match is not proof of existence. Never built implicitly.
102
+ - `unreal_context` — include paths, C++ standard, compiler, the real `WITH_*` defines read from
103
+ `Definitions.<Module>.h`, and the module's public/private dependency lists. Scope is derived from
104
+ path shape, so `Engine/Plugins/.../Public/X.h` is searched as engine scope, and a miss answers the
105
+ path question separately from the symbol question.
106
+ - `unreal_index` — build/refresh/patch/status, `moduleRefs` for the dependency graph, and the
107
+ UnrealBuildTool `-Mode=Query` cache underneath.
108
+
109
+ No compiler, no IDE database, nothing written inside the project: the index lives in
110
+ `~/.pi/agent/unreal-cache` (`$PI_UNREAL_CACHE_DIR`). clangd stays the authority for references,
111
+ overrides, and diagnostics — see `ue-symbol-navigation` in a UE project's `.agents/skills/`. An
112
+ empty clangd answer is **evidence**: on an installed engine the compile database has no engine
113
+ translation units, so "clangd found nothing in engine code" describes the setup, not a defect to
114
+ route around.
115
+
116
+ The boundary this package does not cross: it answers C++ **declaration, module, include-path and
117
+ dependency** facts. Asset questions — UENUM index order, UStruct field types, UCLASS/CDO instance
118
+ values, component lists, parent chains, Blueprint graphs — need the `unreal-engine` editor MCP with
119
+ the editor running. A C++ scan cannot see them; ask a scan only what a scan can answer.
120
+
121
+ ## Conventions this package enforces
122
+
123
+ - `.uasset` and `.umap` are cooked binary — author them in the running editor, never by editing
124
+ bytes. The gate blocks such writes.
125
+ - Newly compiled C++ does not load into a running editor: close the editor, then build with
126
+ `UnrealEngine-BuildProjectEditor`. The gate warns when `UnrealEditor*` is running during a C++ edit.
127
+ - Never run `clang-format` on `*.cs`: it applies the C# language rule and can empty the file
128
+ (verified on `Source/Lp.Target.cs`). The gate blocks such commands. Format C++ with
129
+ `Script/Dev/Format-Cpp.ps1`.