@aeonsparx/pi-unreal 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +202 -2
- package/docs/internals.md +184 -0
- package/extensions/unreal/index.ts +682 -0
- package/extensions/unreal/lib/cache.ts +190 -0
- package/extensions/unreal/lib/compiledb.ts +491 -0
- package/extensions/unreal/lib/descriptors.ts +631 -0
- package/extensions/unreal/lib/engine-resolver.ts +443 -0
- package/extensions/unreal/lib/pi-tools.ts +402 -0
- package/extensions/unreal/lib/plugin-resolver.ts +305 -0
- package/extensions/unreal/lib/query.ts +272 -0
- package/extensions/unreal/lib/registry.ts +208 -0
- package/extensions/unreal/lib/scan.ts +510 -0
- package/extensions/unreal/lib/store.ts +900 -0
- package/extensions/unreal/lib/tools.ts +614 -0
- package/extensions/unreal/lib/ubt.ts +373 -0
- package/extensions/unreal/lib/util.ts +210 -0
- package/package.json +59 -4
- package/prompts/unreal-context.md +26 -0
- package/skills/ue-engine-resolution/SKILL.md +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aeonsparx Interactive
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,203 @@
|
|
|
1
|
-
#
|
|
1
|
+
# pi-unreal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Unreal Engine awareness for the Pi coding agent.**
|
|
4
|
+
|
|
5
|
+
Open a UE project in a `pi` session and the agent stops guessing. It knows which engine
|
|
6
|
+
installation that `.uproject` really resolves to, what the `.uproject` / `.uplugin` files actually
|
|
7
|
+
say, which plugins exist on disk, and where a C++ symbol is declared — with the file, the line and
|
|
8
|
+
the owning module. No compiler, no Visual Studio, no Rider, no clangd required.
|
|
9
|
+
|
|
10
|
+
It is **read-only**: it never writes to your project, never touches the Windows registry, never
|
|
11
|
+
launches the editor, and blocks three kinds of edit that damage Unreal work.
|
|
12
|
+
|
|
13
|
+
## What you get in a session
|
|
14
|
+
|
|
15
|
+
**Context is detected for you.** When the session starts in a directory that contains a
|
|
16
|
+
`.uproject` — the working directory itself, or any of up to 3 parent directories — one note is
|
|
17
|
+
shown before the first answer: the resolved engine root and version, installed vs source build,
|
|
18
|
+
the project's modules, the number of declared plugin references, and the conventions below. Ask
|
|
19
|
+
for a re-check any time with `/unreal`.
|
|
20
|
+
|
|
21
|
+
**Ask in plain English.** "Which engine is this project on?", "Is MassGameplay available to this
|
|
22
|
+
project?", "Where is `FLpMovementDirectionThresholds` defined?", "What do I need to link to use
|
|
23
|
+
`UAnimInstance`?" The agent picks the matching tool:
|
|
24
|
+
|
|
25
|
+
| Tool | Question it answers |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `unreal_resolve_engine` | "Which engine does this `.uproject` use — and how do you know?" |
|
|
28
|
+
| `unreal_project_inspect` | Modules, target platforms, and every plugin reference resolved against the plugins that exist on disk |
|
|
29
|
+
| `unreal_plugin_inspect` | One `.uplugin` as the engine parses it: modules, loading phases, version fields, plugin dependencies |
|
|
30
|
+
| `unreal_symbol` | "Where is this class / struct / function / `UPROPERTY` / delegate declared?" One line per hit: kind, file:line, module |
|
|
31
|
+
| `unreal_symbol_list` | "What does this project define?" Names by prefix — `ULp`, `ANiMy`, `UAnim` |
|
|
32
|
+
| `unreal_context` | "What does this file/module include, define, and depend on?" Include paths, dependency lists, the real `WITH_*` defines |
|
|
33
|
+
| `unreal_index` | Builds and reports the source index, and the clangd compile database on request |
|
|
34
|
+
|
|
35
|
+
**Slash commands you can type yourself:**
|
|
36
|
+
|
|
37
|
+
| Command | Result |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `/unreal [project]` | Engine, modules and plugin-reference report for this directory |
|
|
40
|
+
| `/unreal-engines` | Every engine installation this machine can prove, and how each was found |
|
|
41
|
+
| `/unreal-symbol <symbol>` | Definition lookup, project + plugins, then engine if an engine index exists |
|
|
42
|
+
| `/unreal-module <Module>` | Module directory, public/private dependency lists, defines file, C++ standard, SDK |
|
|
43
|
+
| `/unreal-cache stats\|clear` | Inspect or clear the cached indexes |
|
|
44
|
+
|
|
45
|
+
Example — `/unreal`:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
project C:/Work/Lp/Lp.uproject
|
|
49
|
+
engine: C:/Program Files/Epic Games/UE_5.8
|
|
50
|
+
version 5.8.3 | installed build | association "5.8" (version) via dir-scan
|
|
51
|
+
Build.version: C:/Program Files/Epic Games/UE_5.8/Engine/Build/Build.version
|
|
52
|
+
modules: Lp (Runtime), LpEditor (Editor)
|
|
53
|
+
plugins declared: 25
|
|
54
|
+
plugin index: 897 .uplugin (cache hit)
|
|
55
|
+
plugin references:
|
|
56
|
+
MassGameplay: resolved ← Engine
|
|
57
|
+
ModularGameplay: resolved ← Engine
|
|
58
|
+
SomeLocalPlugin: resolved ← Project
|
|
59
|
+
RetiredPlugin: missing
|
|
60
|
+
totals: 22 resolved, 3 missing of 25 references
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Example — `unreal_symbol` for an engine type (engine index already built):
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
unreal_symbol 'AActor' scope=engine: 1 definition(s)
|
|
67
|
+
class AActor C:/Program Files/Epic Games/UE_5.8/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h:282 [Engine · engine]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
That is a definition line, not a grep dump: grepping a hot engine class name returns ~176 files of
|
|
71
|
+
noise, which is exactly what burns a session's context.
|
|
72
|
+
|
|
73
|
+
## Guardrails (automatic, nothing to enable)
|
|
74
|
+
|
|
75
|
+
| Situation | What happens |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| An edit targeting `*.uasset` / `*.umap` | **Blocked** — those are cooked binary assets; author them in the running editor |
|
|
78
|
+
| A C++ edit while `UnrealEditor` is running | **Warning** — freshly compiled C++ will not load into a running editor; close it, then build |
|
|
79
|
+
| `clang-format` aimed at a `*.cs` file | **Blocked** — clang-format applies the C# rule and can empty the file |
|
|
80
|
+
|
|
81
|
+
## Costs: what is fast, and what is opt-in
|
|
82
|
+
|
|
83
|
+
| Operation | Time |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| Cached lookup (symbol, project facts) | ~0.3 s |
|
|
86
|
+
| First project/plugin index (≈2,400 files) | seconds |
|
|
87
|
+
| Engine `Runtime` index — cold | ~1 minute |
|
|
88
|
+
| Engine `Runtime` index — warm | ~6 s |
|
|
89
|
+
| UnrealBuildTool Query facts | ~7 s, then cached |
|
|
90
|
+
| clangd compile database (`unreal_index` → `compiledb`) | minutes, ~1 GB for the engine scope |
|
|
91
|
+
|
|
92
|
+
Because of that model:
|
|
93
|
+
|
|
94
|
+
- Symbol lookups never stall. If a question needs engine sources and no engine index exists, the
|
|
95
|
+
agent reports the cost and how to pay it, and asks you. Say **yes** once per machine —
|
|
96
|
+
`/unreal-cache stats` shows what you have. Engine scans default to `engineAreas: ["Runtime"]`,
|
|
97
|
+
which is enough for gameplay base classes (`AActor`, `UAnimInstance`, `USceneComponent`);
|
|
98
|
+
`Editor` and `Developer` are opt-in and roughly triple the scan.
|
|
99
|
+
- Ask for `unreal_index` with `action: "build"`, `scope: "engine"` when you want engine-wide
|
|
100
|
+
navigation, and `action: "compiledb"` when you want clangd's references/call hierarchy.
|
|
101
|
+
- After a branch switch, a rebase, adding a plugin, or an engine version change, say "rebuild the
|
|
102
|
+
Unreal index" (that is `refresh: true`).
|
|
103
|
+
- Everything lives in `~/.pi/agent/unreal-cache` (override with `$PI_UNREAL_CACHE_DIR`) — never
|
|
104
|
+
inside your project tree or your engine installation. The compile database encodes this
|
|
105
|
+
machine's absolute paths, so it is machine-local by design.
|
|
106
|
+
|
|
107
|
+
## Requirements
|
|
108
|
+
|
|
109
|
+
- **Windows**, with **PowerShell 7** (`pwsh`) for registry-based engine discovery. Without it the
|
|
110
|
+
extension still works: resolution falls back to `UNREAL_ENGINE` / `UE_ROOT` /
|
|
111
|
+
`PI_UNREAL_ENGINE_ROOT`, the Launcher's install list, and a scan of
|
|
112
|
+
`C:/Program Files/Epic Games`.
|
|
113
|
+
- An Unreal Engine installation the project can resolve to (Launcher install or source build).
|
|
114
|
+
Descriptor field handling is transcribed from UE 5.8.
|
|
115
|
+
- Nothing else. No compiler, no IDE language service. clangd is optional enrichment for
|
|
116
|
+
`compiledb` and the `clangd --check` acceptance step.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
| Report | What to do |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `engine: AMBIGUOUS — more than one valid installation matches` | Two roots match the association. Pass the one you want as `engineRootHint` (e.g. `engineRootHint: "C:/Program Files/Epic Games/UE_5.8"`). |
|
|
123
|
+
| `engine: UNRESOLVED` with rejected candidates | Read the reasons — a stale registry key, a missing `Build.version`, a version mismatch. The report lists remediation steps; the extension never writes registry keys for you. |
|
|
124
|
+
| `no .uproject within 4 levels above …` | The search walks up from the working directory. Pass the project explicitly: `project: "C:/Work/Lp/Lp.uproject"` (a directory containing one works too). |
|
|
125
|
+
| `unreal_symbol` finds nothing in scope `engine` | Build the engine index first (`unreal_index` → `action: "build"`, `scope: "engine"`). |
|
|
126
|
+
| Results look stale after a rebase or engine update | Re-run with `refresh: true`, or `/unreal-cache clear`. |
|
|
127
|
+
| `no plugin named "X" in the index` | That reference is genuinely not on disk. `/unreal` shows which roots were scanned. |
|
|
128
|
+
|
|
129
|
+
## What it deliberately does not do
|
|
130
|
+
|
|
131
|
+
- It does not install, register, migrate, or "fix" an engine, and never writes registry keys.
|
|
132
|
+
- It does not build, cook, package, launch the editor, or edit `.ini` files.
|
|
133
|
+
- It does not hand-edit assets, and blocks attempts to.
|
|
134
|
+
- Symbol lookup reports **declarations**. Type resolution, overloads, call sites, inheritance and
|
|
135
|
+
diagnostics are clangd's job when clangd is installed.
|
|
136
|
+
- `.ini` provenance, live-coding state, and asset metadata are out of scope for v1.
|
|
137
|
+
|
|
138
|
+
## Install
|
|
139
|
+
|
|
140
|
+
Published as **`@aeonsparx/pi-unreal`** on npmjs.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pi install npm:@aeonsparx/pi-unreal@1.0.0 # pinned version, reproducible
|
|
144
|
+
pi install git:github.com/Aeonsparx-Interactive/pi-unreal@v1.0 # follows patch releases on the v1.0 line
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The `npm:` route installs from the registry and pins an exact version. The `git:` route clones the
|
|
148
|
+
repository and checks out the ref: a line alias (`@v1.0`) is a moving tag that patch releases
|
|
149
|
+
force-move, so `pi` picks up patches on update; pin `@v1.0.2` for a fixed snapshot. Loading straight
|
|
150
|
+
from a clone still works and needs no install: `pi -e ./extensions/unreal/index.ts`.
|
|
151
|
+
|
|
152
|
+
## Releasing (maintainers)
|
|
153
|
+
|
|
154
|
+
| `package.json` bump | GitHub Release | npmjs |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `major` / `minor` | **new** release `v<major>.<minor>` | staged `<major>.<minor>.<patch>` |
|
|
157
|
+
| `patch` | **updates** the existing `v<major>.<minor>` release | staged `<major>.<minor>.<patch>` |
|
|
158
|
+
|
|
159
|
+
Merge a PR that bumps `version` into `main`. `.github/workflows/release.yml` runs `bun test` on
|
|
160
|
+
Linux and Windows, pushes the immutable tag `v<major>.<minor>.<patch>`, moves the `v<major>.<minor>`
|
|
161
|
+
alias, creates or updates that release, then stages the tarball with `npm stage publish`. **Nothing
|
|
162
|
+
is public until a human approves it** (2FA, from any machine):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm stage list @aeonsparx/pi-unreal # stage ids (no 2FA)
|
|
166
|
+
npm stage view <stage-id> # metadata
|
|
167
|
+
npm stage download <stage-id> # inspect the exact tarball that will publish
|
|
168
|
+
npm stage approve <stage-id> # 2FA - this is the publish act
|
|
169
|
+
npm stage reject <stage-id> # 2FA - frees the staged version number
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The same queue is on npmjs.com (Staged Packages tab). `npm stage` requires npm >= 11.15.0
|
|
173
|
+
(`npm install -g npm@12`) and Node >= 22.14.0. `NPM_TOKEN` (repository secret) is a Granular Access
|
|
174
|
+
Token with Read+Write and **no** 2FA bypass: CI can stage, only you can publish. Staged versions
|
|
175
|
+
occupy their version number, so a rejected stage burns `x.y.z` until `npm stage reject` frees it.
|
|
176
|
+
Provenance is deliberately off in v1 (`--provenance` needs a Provenance-scope token, and the
|
|
177
|
+
attestation path through stage-then-approve is unverified).
|
|
178
|
+
|
|
179
|
+
## Developers
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
bun test # 66 tests: resolver, descriptors, plugins, cache, gate, scanner, index, compile-db pipeline, fixture hygiene
|
|
183
|
+
bun run typecheck # tsc --noEmit
|
|
184
|
+
bun test/live-check.ts "C:/path/to/Project.uproject" # live machine smoke
|
|
185
|
+
bun test/keys.census.mjs "C:/Program Files/Epic Games/UE_5.8/Engine/Plugins"
|
|
186
|
+
bun run tmp/smoke.mjs project "C:/path/to/Project" # symbol index against a real project
|
|
187
|
+
bun run tmp/smoke.mjs query "C:/path/to/Project" # UnrealBuildTool Query round trip
|
|
188
|
+
bun run tmp/smoke.mjs engine "C:/path/to/Project" # Engine/Source index (minutes, opt-in)
|
|
189
|
+
bun run tmp/smoke.mjs compiledb "C:/path/to/Project" # clangd database + `clangd --check` acceptance
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
From a clone, load the extension into a session without installing it:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pi -e ./extensions/unreal/index.ts
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Layout: `extensions/unreal/index.ts` (tools, commands, context card, convention gate),
|
|
199
|
+
`extensions/unreal/lib/` (resolver, descriptors, plugin index, symbol index, UBT Query, compile
|
|
200
|
+
database), `prompts/unreal-context.md`, `skills/ue-engine-resolution/`, `test/`.
|
|
201
|
+
|
|
202
|
+
Design notes and measured internals — engine resolution algorithm, descriptor fidelity, plugin
|
|
203
|
+
discovery, the index and compile-database pipeline — are in [docs/internals.md](docs/internals.md).
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# pi-unreal internals
|
|
2
|
+
|
|
3
|
+
Design and implementation notes: how the tools derive their answers, and the measured numbers behind
|
|
4
|
+
the cost model. Read the README for day-to-day usage; this file is the engine-facing detail that
|
|
5
|
+
the README deliberately leaves out.
|
|
6
|
+
|
|
7
|
+
## Symbol index (no compiler, no IDE)
|
|
8
|
+
|
|
9
|
+
`unreal_symbol`, `unreal_context`, and `unreal_index` answer navigation questions from two
|
|
10
|
+
compiler-free sources:
|
|
11
|
+
|
|
12
|
+
1. **UnrealBuildTool Query** (`-Mode=Query`: `TargetDetails`, `TargetModuleReferences`) — the
|
|
13
|
+
engine's own record of targets, modules, module dependencies, include paths, and forced
|
|
14
|
+
includes. Measured on UE 5.8: 6.0 s for 1,079 modules, 2.1 s for the dependency graph, and it
|
|
15
|
+
needs no compiler. That is why MSVC-only machines (Visual Studio, Rider) are first class here:
|
|
16
|
+
Visual Studio's `Browse.VC.db` is encrypted with no public API, Rider's resolver is in-process
|
|
17
|
+
through RiderLink, and both derive their context from the same UBT rules this layer reads.
|
|
18
|
+
2. **A regex definition scanner** over the project, its enabled plugins, and the engine areas its
|
|
19
|
+
modules actually depend on — `class`/`struct`/`enum`/`union`, `UFUNCTION`, `UPROPERTY`,
|
|
20
|
+
delegates, aliases, with `Owner:Member` qualified names and forward declarations marked.
|
|
21
|
+
|
|
22
|
+
Artifacts live in `$PI_UNREAL_CACHE_DIR/indexes/<key>/` (`meta.json`, `defs.tsv`, `files.tsv`,
|
|
23
|
+
plus `includes.tsv` in `full` mode): plain TSV — greppable, inspectable, shared across sessions
|
|
24
|
+
and projects — and never inside the project tree or the engine installation.
|
|
25
|
+
|
|
26
|
+
Measured on this box (UE 5.8.3 installed engine; Lp project with 25 enabled plugins):
|
|
27
|
+
|
|
28
|
+
| Scope | Files | Definitions | Cold build |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| Project + plugins | 2,405 | 38,901 | 6.7 s |
|
|
31
|
+
| Engine `Runtime` (shipped default: 279 module roots) | 18,566 | 436,729 | 57.6 s cold, 5.7 s warm |
|
|
32
|
+
| Engine `Runtime/Core` + `Runtime/CoreUObject` | 3,094 | 70,548 | 16 s |
|
|
33
|
+
| Cached lookup | — | — | ~0.3 s |
|
|
34
|
+
|
|
35
|
+
Cost discipline is the design. A lookup builds the project index (seconds) and uses an engine
|
|
36
|
+
index only if one exists; `scope: "engine"` on a cold machine returns
|
|
37
|
+
`engine index not built — build it with unreal_index (action: "build", scope: "engine")` instead of
|
|
38
|
+
spending minutes on a scan nobody asked for. `unreal_index` is the explicit, paid path.
|
|
39
|
+
|
|
40
|
+
Ranking inside `unreal_symbol`: exact (0) → qualified member (1) → prefix (2) → substring (3).
|
|
41
|
+
Forward declarations rank last, so `unreal_symbol AActor` returns
|
|
42
|
+
`Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h:282` (the `UCLASS` declaration), not
|
|
43
|
+
the `class AActor;` line in `PhysicsCollision.h`.
|
|
44
|
+
`unreal_symbol LpGameModeBase` finds `ALpGameModeBase` (the UE prefix is added for you); `unreal_symbol FLpMovementDirectionThresholds:F` finds a single-letter `UPROPERTY` field; `unreal_context LpCharacterTypes.h` resolves a bare file name through the index. Engine areas are derived from module
|
|
45
|
+
dependencies (`.uproject` modules → recursive `PublicDependencyModuleNames` → plugin-supplied
|
|
46
|
+
modules → `Engine/Source/<area>/*`), so the index contains what this project can compile against,
|
|
47
|
+
not the whole engine.
|
|
48
|
+
|
|
49
|
+
One engine fact worth knowing: UBT Query reports `"defines": []` for every module. The effective
|
|
50
|
+
preprocessor set lives in a forced include —
|
|
51
|
+
`Intermediate/Build/<Platform>/<Config>/<Target>/Development/<Module>/Definitions.<Module>.h` —
|
|
52
|
+
and `unreal_context` reads that file instead of guessing `WITH_*` values. Query is also never
|
|
53
|
+
run with `-NoIntellisense`: that flag produces a syntactically valid skeleton with an empty
|
|
54
|
+
`moduleToCompileSettings`, and `payloadOk()` rejects it.
|
|
55
|
+
|
|
56
|
+
## clangd enrichment (`unreal_index` action `compiledb`)
|
|
57
|
+
|
|
58
|
+
The definition index is the compiler-free path. When you want clangd's semantic power (references,
|
|
59
|
+
call hierarchy, diagnostics), the database it needs comes from the engine: `UnrealBuildTool
|
|
60
|
+
-Modes=GenerateClangDatabase`, normalized in the pi-unreal cache.
|
|
61
|
+
|
|
62
|
+
The UnrealBuildTool invocation is the part nobody documents, so it is encoded in one place
|
|
63
|
+
(`lib/compiledb.ts`):
|
|
64
|
+
|
|
65
|
+
- The target is **three positional tokens** — `Win64 Development LpEditor`. `TargetDescriptor` only
|
|
66
|
+
inspects arguments that do not start with `-`, and its `+` split requires every token to be the
|
|
67
|
+
same kind, so `-Target=LpEditor` fails with `No platforms specified for target` and
|
|
68
|
+
`Win64+Development+LpEditor` fails with `The platform name Development is not a valid platform
|
|
69
|
+
name`. `-Platform=`/`-Configuration=` set `BuildConfiguration` only and produce a duplicate
|
|
70
|
+
platform error next to the positional tokens.
|
|
71
|
+
- `-OutputDir=` must already exist (UnrealBuildTool throws
|
|
72
|
+
`DirectoryNotFoundException: Could not find a part of the path ...\compile_commands.json`), and
|
|
73
|
+
the mode's own `-NoPCH -NoVFS -Compiler=Clang` additions mean the emitted driver is
|
|
74
|
+
`clang-cl.exe`, not `cl.exe`.
|
|
75
|
+
- `-Include=` rules match nothing useful (measured: `Source/Lp`, `*/Source/Lp/*`,
|
|
76
|
+
`C:/Users/.../Source/Lp/**` all yield an empty database), so scoping happens after generation.
|
|
77
|
+
|
|
78
|
+
Normalization is Epic's `stripPostPch.py` recipe, ported: expand the per-module `@file.rsp`
|
|
79
|
+
response files, drop `/Yu* /Fm* /Fe* /Fp*` and everything after `/link`, drop PCH translation
|
|
80
|
+
units (`SharedPCH.*.h.cpp`, `PCH.*.h.cpp`) and header entries, then keep the requested scope. The
|
|
81
|
+
database is written to `$PI_UNREAL_CACHE_DIR/compiledb/<target>.<platform>.<config>/` — it encodes
|
|
82
|
+
this machine's absolute paths and toolchain version, so it is a machine artifact, never a repo one.
|
|
83
|
+
|
|
84
|
+
`check: "<file>"` runs `clangd --check` against the result, which is the acceptance test. Measured
|
|
85
|
+
on Lp (UE 5.8.3, installed engine, VS 18 LLVM clangd): UnrealBuildTool 1.5-1.7 s, 4 entries,
|
|
86
|
+
4 response files expanded, 0 switches dropped; `clangd --check` built a 62 MB preamble and
|
|
87
|
+
reported **`All checks completed, 0 errors`** in ~2.6 s.
|
|
88
|
+
|
|
89
|
+
The honest limits of that 4-entry number: an installed (precompiled) engine contributes no compile
|
|
90
|
+
actions, so the database covers the project's own translation units, not `Engine/Source`. Engine
|
|
91
|
+
files still work under clangd because the project's `.clangd` supplies the include paths — exactly
|
|
92
|
+
what Epic's Unreal-clangd VS Code extension does (its generated `Lp.0.rsp` contains only `/FI`
|
|
93
|
+
plus 523 `/I` lines, and `compile_commands.json` there has 6 project entries and no engine entry).
|
|
94
|
+
Re-run with `refresh: true` after changing `Build.cs` files, adding modules, or switching engine
|
|
95
|
+
version.
|
|
96
|
+
|
|
97
|
+
## How engine resolution works
|
|
98
|
+
|
|
99
|
+
Mirrors `FDesktopPlatformWindows::GetAvailableEngineDescriptions`
|
|
100
|
+
(`Engine/Source/Developer/DesktopPlatform/Private/DesktopPlatformBase.cpp:459-511`) and
|
|
101
|
+
`VerifyEngineGuid` (`DesktopPlatformWindows.cpp:615-664`):
|
|
102
|
+
|
|
103
|
+
| `.uproject` association | Source of truth |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| engine-root path | used directly, validated |
|
|
106
|
+
| `5.8` (Launcher install) | `HKLM:\SOFTWARE\EpicGames\Unreal Engine\<ver>` → `InstalledDirectory`, then `<installDirDefault>\UE_<ver>` |
|
|
107
|
+
| `{GUID}` (source build) | `HKCU:\SOFTWARE\Epic Games\Unreal Engine\Builds` |
|
|
108
|
+
| empty | walk up 12 levels looking for `Engine/Config/BaseEngine.ini` |
|
|
109
|
+
|
|
110
|
+
Below the association: `LauncherInstalled.dat`, `HKLM` versions, `UNREAL_ENGINE` / `UE_ROOT` /
|
|
111
|
+
`PI_UNREAL_ENGINE_ROOT`, and a scan of `C:/Program Files/Epic Games` for `UE_*`. Explicit
|
|
112
|
+
`engineRootHint` outranks every registry source.
|
|
113
|
+
|
|
114
|
+
Every candidate is validated before use — `Engine/Binaries` + `Engine/Build`
|
|
115
|
+
(`DesktopPlatformBase.cpp:401-421`) **and** `Engine/Build/Build.version` matching the
|
|
116
|
+
association. Stale registry keys and env pointers are reported as rejected candidates with the
|
|
117
|
+
reason, never silently dropped. One survivor → `resolved`; several → `ambiguous`; none →
|
|
118
|
+
`unresolved` + remediation. Nothing is ever written back to the registry.
|
|
119
|
+
|
|
120
|
+
Registry reads run through one memoized `pwsh -NoProfile` snapshot (8 s timeout). If `pwsh` is
|
|
121
|
+
missing the snapshot degrades to `available: false` and the resolver continues with
|
|
122
|
+
file-based sources — the extension never fails at import time.
|
|
123
|
+
|
|
124
|
+
## Plugin discovery
|
|
125
|
+
|
|
126
|
+
Mirrors `UnrealBuildTool.System.ReadAvailablePlugins` (`System/Plugins.cs:322`):
|
|
127
|
+
Engine `Engine/Plugins` → Project `Plugins/` → Mod `Mods/` → `AdditionalPluginDirectories`,
|
|
128
|
+
with later scans shadowing earlier ones on name collision. `Binaries/`, `Intermediate/`,
|
|
129
|
+
`Saved/`, `.git/`, `ThirdParty/` are skipped. Reference semantics mirror `PluginManager`:
|
|
130
|
+
`Enabled` / `Optional` / `Activate` (`PluginReferenceDescriptor.cpp:132-172`); an engine-version
|
|
131
|
+
mismatch is a warning, not an error (`PluginManager.cpp:2690-2712`).
|
|
132
|
+
|
|
133
|
+
Indexes are cached on disk keyed by the mtime of every scanned directory plus the engine
|
|
134
|
+
changelist, with a 10-minute backstop (`MAX_AGE_MS`) because content-only `.uplugin` edits do
|
|
135
|
+
not move the parent directory's mtime.
|
|
136
|
+
|
|
137
|
+
## Descriptor fidelity
|
|
138
|
+
|
|
139
|
+
Field names, host types, and loading phases are transcribed from the engine's serializers
|
|
140
|
+
(UE 5.8): `ProjectDescriptor.cpp:100-225`, `PluginDescriptor.cpp:263-409`,
|
|
141
|
+
`ModuleDescriptor.cpp`, `PluginReferenceDescriptor.cpp:130-180`, cross-checked against 897
|
|
142
|
+
`.uplugin` files in UE 5.8 `Engine/Plugins`. Consequences:
|
|
143
|
+
|
|
144
|
+
- `.uproject` / `.uplugin` are parsed as JSONC (comments and trailing commas are legal in the
|
|
145
|
+
engine's reader).
|
|
146
|
+
- `Modules` is an array of objects keyed by `Name`; `Type` is required
|
|
147
|
+
(`Found Module entry 'X' with a missing 'Type' field`), `LoadingPhase` defaults to `Default`.
|
|
148
|
+
- A plugin reference without `"Enabled"` is rejected by UE 5.8 — reported, not guessed.
|
|
149
|
+
- Unknown keys are reported as `… is not serialized by FPluginDescriptor`, and the known-key
|
|
150
|
+
set is wide enough (including legacy `Whitelist*`/`Blacklist*` and pre-5.x keys) that genuine
|
|
151
|
+
engine files produce no noise.
|
|
152
|
+
- `.uplugin` has no `Enabled` key; enablement is `EnabledByDefault` + the project reference.
|
|
153
|
+
- `IsBetaVersion` is metadata in UE 5.8 (no engine/UBT beta gate exists).
|
|
154
|
+
|
|
155
|
+
## Known limits
|
|
156
|
+
|
|
157
|
+
- Registry access needs `pwsh` (PowerShell 7). Without it, registry sources are unavailable and
|
|
158
|
+
resolution relies on env, directory scans, and inline associations.
|
|
159
|
+
- `unreal_resolve_engine` reports what the machine can prove. It does not install, register, or
|
|
160
|
+
migrate engines, and never writes registry keys (the engine's `RegisterEngineInstallation`
|
|
161
|
+
does; this package deliberately does not).
|
|
162
|
+
- v1 is inspection only. `.ini` provenance is out of scope.
|
|
163
|
+
- The definition index is a scanner, not a parser: it finds declarations (`class`, `struct`,
|
|
164
|
+
`enum`, `union`, `UFUNCTION`/`UPROPERTY`/`UCLASS`-marked members, delegates, aliases) and
|
|
165
|
+
records `Owner:Member` qualified names. It does not resolve types, overloads, call sites, or
|
|
166
|
+
inheritance — that is what clangd is for when it is installed. Macro-heavy code can still be
|
|
167
|
+
misread: the scanner is regex-based, so a definition hidden inside an unusual macro spelling may
|
|
168
|
+
be missed, and `#if` branches are all recorded (a symbol declared in a disabled branch is a
|
|
169
|
+
candidate, not a guarantee).
|
|
170
|
+
- `moduleRefs` and `includePaths` come from UnrealBuildTool Query, which needs a buildable
|
|
171
|
+
target for the project; when Query fails the index still builds and says so.
|
|
172
|
+
- The engine index is never built implicitly: scans are opt-in through `unreal_index`.
|
|
173
|
+
- `action: "compiledb"` needs UnrealBuildTool (so an engine installation) and, for the
|
|
174
|
+
`clangd --check` acceptance step, a clangd binary — `CLANGD_PATH`, the Visual Studio LLVM
|
|
175
|
+
toolset, the WinGet LLVM package, or `C:/Program Files/LLVM/bin`. Without clangd the database is
|
|
176
|
+
still produced and reported; only the check is skipped.
|
|
177
|
+
- On an installed engine the compile database covers the project's translation units (engine
|
|
178
|
+
modules are precompiled and contribute no compile actions). The engine-wide scope of
|
|
179
|
+
`project+engine` is the source-built case, and there it is a ~1 GB artifact taking minutes.
|
|
180
|
+
- Engine area selection is a cost dial, not a completeness claim. `DEFAULT_ENGINE_AREAS =
|
|
181
|
+
["Runtime"]` covers every gameplay type (`AActor`, `UAnimInstance`, `USceneComponent`) at
|
|
182
|
+
18,566 files; `Editor` and `Developer` are opt-in via `engineAreas` and roughly triple the scan.
|
|
183
|
+
- Index reuse requires the *same root set*: an index built over two modules is never used to answer
|
|
184
|
+
a `Runtime`-wide query. Partial coverage reported as an answer is worse than a rebuild.
|